/* ptp.c
 *
 * Copyright (C) 2001-2004 Mariusz Woloszyn <emsi@ipartners.pl>
 * Copyright (C) 2003-2024 Marcus Meissner <marcus@jet.franken.de>
 * Copyright (C) 2006-2008 Linus Walleij <triad@df.lth.se>
 * Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
 * Copyright (C) 2009-2024 Axel Waggershauser <awagger@web.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

#define _DEFAULT_SOURCE
#include "config.h"
#include "ptp.h"

#ifdef HAVE_LIBXML2
# include <libxml/parser.h>
#endif

#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "libgphoto2/i18n.h"
#include "libgphoto2_port/compiletime-assert.h"

#define CHECK_PTP_RC(RESULT) do { uint16_t r = (RESULT); if (r != PTP_RC_OK) return r; } while(0)

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

/* Initialize a PTPContainer struct. For the usage, see the PTP_CNT_INIT() macro below. */
static inline
void ptp_init_container(PTPContainer* ptp, int n_param, ...)
{
	va_list	args;
	int	i;

	memset(ptp, 0, sizeof(*ptp));
	va_start(args, n_param);
	/* verify the int value cannot have cut off the uint16_t value */
	COMPILETIME_ASSERT(INT_MAX >= UINT16_MAX);
	/* note that uint16_t can be promoted to an int, so va_arg
	 * needs to be called with the int type */
	ptp->Code   = (uint16_t)(va_arg(args, int));
	ptp->Nparam = n_param;

	/* Make certain the memory layout of PTPContainer matches what
	 * the for loop code below expects.
	 */
	COMPILETIME_ASSERT((offsetof(PTPContainer, Param1) + sizeof(uint32_t))
			   == offsetof(PTPContainer, Param2));
	COMPILETIME_ASSERT((offsetof(PTPContainer, Param2) + sizeof(uint32_t))
			   == offsetof(PTPContainer, Param3));
	COMPILETIME_ASSERT((offsetof(PTPContainer, Param3) + sizeof(uint32_t))
			   == offsetof(PTPContainer, Param4));
	COMPILETIME_ASSERT((offsetof(PTPContainer, Param4) + sizeof(uint32_t))
			   == offsetof(PTPContainer, Param5));

	COMPILETIME_ASSERT((offsetof(PTPContainer, Param1) + 5*sizeof(uint32_t))
			   == offsetof(PTPContainer, Nparam));

	/* Silently ignore parameters past Param5 */
	for (i=0; (i<n_param) && (i<5); ++i) {
		(&ptp->Param1)[i] = va_arg(args, uint32_t);
	}
	va_end(args);
}


#define N_PARAM_SEQ(DUMMY,				\
		    Pa, Pb, Pc, Pd, Pe,			\
		    P0, P1, P2, P3, P4, P5,		\
		    N, ...)				\
	N


#define N_PARAM(...)				\
	N_PARAM_SEQ(__VA_ARGS__,		\
		    "a", "b", "c", "d", "e",	\
		    5U, 4U, 3U, 2U, 1U, 0U, NULL)


/* Usage: PTP_CNT_INIT(PTP, CODE[, PARAM...])
 *
 * Together with ptp_init_container(), this basically is a shortcut to
 * initializing a PTPContainer struct, but automatically counts the
 * number of PTPContainer parameters (members Param1 through Param5)
 * set.
 *
 * The valid number of parameters is 0 through 5.
 */
#define PTP_CNT_INIT(PTP, ...)						\
	do {								\
		const int n_param = N_PARAM(-666, __VA_ARGS__);		\
		ptp_init_container(&PTP, n_param, __VA_ARGS__);		\
	} while (0)


static uint16_t ptp_exit_recv_memory_handler (PTPDataHandler*,unsigned char**,unsigned long*);
static uint16_t ptp_init_recv_memory_handler(PTPDataHandler*);
static uint16_t ptp_init_send_memory_handler(PTPDataHandler*,unsigned char*,unsigned long len);
static uint16_t ptp_exit_send_memory_handler (PTPDataHandler *handler);

void
ptp_debug (PTPParams *params, const char *format, ...)
{
	va_list args;

	va_start (args, format);
	if (params->debug_func!=NULL)
		params->debug_func (params->data, format, args);
	else
	{
		vfprintf (stderr, format, args);
		fprintf (stderr,"\n");
		fflush (stderr);
	}
	va_end (args);
}

void
ptp_error (PTPParams *params, const char *format, ...)
{
	va_list args;

	va_start (args, format);
	if (params->error_func!=NULL)
		params->error_func (params->data, format, args);
	else
	{
		vfprintf (stderr, format, args);
		fprintf (stderr,"\n");
		fflush (stderr);
	}
	va_end (args);
}

/* Helper function to quickly render some bytes into a static string
 * buffer for immediate copying/snprintf-ing. Parameter fmt is the
 * format string used for each byte, e.g. "%02x ". */
const char*
ptp_bytes2str(const uint8_t *data, int data_size, const char *fmt)
{
	static char line[16 * 3 + 1];
	int pos = 0;
	for (int i = 0; i < data_size && pos < (int)sizeof(line); ++i) {
		if (data[i] || fmt)
			pos += snprintf(line + pos, sizeof(line) - pos, fmt ? fmt : "%02x ", data[i]);
		else
			pos += snprintf(line + pos, sizeof(line) - pos, " - ");
	}
	return line;
}

void
ptp_debug_data(PTPParams *params, const uint8_t* data, int size)
{
	uint8_t zeros[16] = { 0 };
	int zero_lines = 0;
	for (int k = 0; k < size; k += 16) {
		zero_lines = (size - k > 16 && memcmp(data + k, zeros, 16) == 0) ? zero_lines + 1 : 0;
		if (zero_lines < 2)
			ptp_debug (params, "         0x%03x: %s", k, ptp_bytes2str(data + k, MIN(16, size - k), NULL));
		else if (zero_lines == 2)
			ptp_debug (params, "         [...]: %s", ""); //ptp_bytes2str(zeros, 16, "%02x "));
	}
}

/* Pack / unpack functions */

#include "ptp-pack.c"

/* major PTP functions */

/**
 * ptp_transaction:
 * params:	PTPParams*
 * 		PTPContainer* ptp	- general ptp container
 * 		uint16_t flags		- lower 8 bits - data phase description
 * 		unsigned int sendlen	- senddata phase data length
 * 		char** data		- send or receive data buffer pointer
 * 		int* recvlen		- receive data length
 *
 * Performs PTP transaction. ptp is a PTPContainer with appropriate fields
 * filled in (i.e. operation code and parameters). It's up to caller to do
 * so.
 * The flags decide whether the transaction has a data phase and what is its
 * direction (send or receive).
 * If transaction is sending data the sendlen should contain its length in
 * bytes, otherwise it's ignored.
 * The data should contain an address of a pointer to data going to be sent
 * or is filled with such a pointer address if data are received depending
 * od dataphase direction (send or received) or is being ignored (no
 * dataphase).
 * The memory for a pointer should be preserved by the caller, if data are
 * being retrieved the appropriate amount of memory is being allocated
 * (the caller should handle that!).
 *
 * Return values: Some PTP_RC_* code.
 * Upon success PTPContainer* ptp contains PTP Response Phase container with
 * all fields filled in.
 **/
uint16_t
ptp_transaction_new (PTPParams* params, PTPContainer* ptp,
		     uint16_t flags, uint64_t sendlen,
		     PTPDataHandler *handler
) {
	int 		tries;
	uint16_t	cmd;

	if ((params==NULL) || (ptp==NULL))
		return PTP_ERROR_BADPARAM;

	cmd = ptp->Code;
	ptp->Transaction_ID=params->transaction_id++;
	ptp->SessionID=params->session_id;
	/* send request */
	CHECK_PTP_RC(params->sendreq_func (params, ptp, flags));
	/* is there a dataphase? */
	switch (flags&PTP_DP_DATA_MASK) {
	case PTP_DP_SENDDATA:
		{
			uint16_t ret = params->senddata_func(params, ptp, sendlen, handler);
			if (ret == PTP_ERROR_CANCEL)
				CHECK_PTP_RC(params->cancelreq_func(params, params->transaction_id-1));
			CHECK_PTP_RC(ret);
		}
		break;
	case PTP_DP_GETDATA:
		{
			uint16_t ret = params->getdata_func(params, ptp, handler);
			if (ret == PTP_ERROR_CANCEL)
				CHECK_PTP_RC(params->cancelreq_func(params, params->transaction_id-1));
			CHECK_PTP_RC(ret);
		}
		break;
	case PTP_DP_NODATA:
		break;
	default:
		return PTP_ERROR_BADPARAM;
	}
	tries = 3;
	while (tries--) {
		uint16_t ret;
		/* get response */
		ret = params->getresp_func(params, ptp);
		if (ret == PTP_ERROR_RESP_EXPECTED) {
			ptp_debug (params,"PTP: response expected but not got, retrying.");
			tries++;
			continue;
		}
		CHECK_PTP_RC(ret);

		if (ptp->Transaction_ID < params->transaction_id-1) {
			/* The Leica uses Transaction ID 0 on result from CloseSession. */
			if (cmd == PTP_OC_CloseSession)
				break;
			tries++;
			ptp_debug (params,
				"PTP: Sequence number mismatch %d vs expected %d, suspecting old reply.",
				ptp->Transaction_ID, params->transaction_id-1
			);
			continue;
		}
		if (ptp->Transaction_ID != params->transaction_id-1) {
			/* try to clean up potential left overs from previous session */
			if ((cmd == PTP_OC_OpenSession) && tries)
				continue;
			ptp_error (params,
				"PTP: Sequence number mismatch %d vs expected %d.",
				ptp->Transaction_ID, params->transaction_id-1
			);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
			return PTP_ERROR_BADPARAM;
#endif
		}
		break;
	}
	return ptp->Code;
}

/* memory data get/put handler */
typedef struct {
	unsigned char	*data;
	unsigned long	size, curoff;
} PTPMemHandlerPrivate;

static uint16_t
memory_getfunc(PTPParams* params, void* private,
	       unsigned long wantlen, unsigned char *data,
	       unsigned long *gotlen
) {
	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;
	unsigned long tocopy = wantlen;

	if (priv->curoff + tocopy > priv->size)
		tocopy = priv->size - priv->curoff;
	memcpy (data, priv->data + priv->curoff, tocopy);
	priv->curoff += tocopy;
	*gotlen = tocopy;
	return PTP_RC_OK;
}

static uint16_t
memory_putfunc(PTPParams* params, void* private,
	       unsigned long sendlen, unsigned char *data
) {
	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;

	if (priv->curoff + sendlen > priv->size) {
		priv->data = realloc (priv->data, priv->curoff+sendlen);
		if (!priv->data)
			return PTP_RC_GeneralError;
		priv->size = priv->curoff + sendlen;
	}
	memcpy (priv->data + priv->curoff, data, sendlen);
	priv->curoff += sendlen;
	return PTP_RC_OK;
}

/* init private struct for receiving data. */
static uint16_t
ptp_init_recv_memory_handler(PTPDataHandler *handler)
{
	PTPMemHandlerPrivate* priv;
	priv = malloc (sizeof(PTPMemHandlerPrivate));
	if (!priv)
		return PTP_RC_GeneralError;
	handler->priv = priv;
	handler->getfunc = memory_getfunc;
	handler->putfunc = memory_putfunc;
	priv->data = NULL;
	priv->size = 0;
	priv->curoff = 0;
	return PTP_RC_OK;
}

/* init private struct and put data in for sending data.
 * data is still owned by caller.
 */
static uint16_t
ptp_init_send_memory_handler(PTPDataHandler *handler,
	unsigned char *data, unsigned long len
) {
	PTPMemHandlerPrivate* priv;
	priv = malloc (sizeof(PTPMemHandlerPrivate));
	if (!priv)
		return PTP_RC_GeneralError;
	handler->priv = priv;
	handler->getfunc = memory_getfunc;
	handler->putfunc = memory_putfunc;
	priv->data = data;
	priv->size = len;
	priv->curoff = 0;
	return PTP_RC_OK;
}

/* free private struct + data */
static uint16_t
ptp_exit_send_memory_handler (PTPDataHandler *handler)
{
	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)handler->priv;
	/* data is owned by caller */
	free (priv);
	return PTP_RC_OK;
}

/* hand over our internal data to caller */
static uint16_t
ptp_exit_recv_memory_handler (PTPDataHandler *handler,
	unsigned char **data, unsigned long *size
) {
	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)handler->priv;
	*data = priv->data;
	*size = priv->size;
	free (priv);
	return PTP_RC_OK;
}

/* fd data get/put handler */
typedef struct {
	int fd;
} PTPFDHandlerPrivate;

static uint16_t
fd_getfunc(PTPParams* params, void* private,
	unsigned long wantlen, unsigned char *data,
	unsigned long *gotlen
) {
	PTPFDHandlerPrivate* priv = (PTPFDHandlerPrivate*)private;
	int		got;

	got = read (priv->fd, data, wantlen);
	if (got != -1)
		*gotlen = got;
	else
		return PTP_RC_GeneralError;
	return PTP_RC_OK;
}

static uint16_t
fd_putfunc(PTPParams* params, void* private,
	unsigned long sendlen, unsigned char *data
) {
	ssize_t	written;
	PTPFDHandlerPrivate* priv = (PTPFDHandlerPrivate*)private;

	written = write (priv->fd, data, sendlen);
	if ((unsigned long)written != sendlen)
		return PTP_ERROR_IO;
	return PTP_RC_OK;
}

static uint16_t
ptp_init_fd_handler(PTPDataHandler *handler, int fd)
{
	PTPFDHandlerPrivate* priv;
	priv = malloc (sizeof(PTPFDHandlerPrivate));
	if (!priv)
		return PTP_RC_GeneralError;
	handler->priv = priv;
	handler->getfunc = fd_getfunc;
	handler->putfunc = fd_putfunc;
	priv->fd = fd;
	return PTP_RC_OK;
}

static uint16_t
ptp_exit_fd_handler (PTPDataHandler *handler)
{
	PTPFDHandlerPrivate* priv = (PTPFDHandlerPrivate*)handler->priv;
	free (priv);
	return PTP_RC_OK;
}

/* Old style transaction, based on memory */
/* A note on memory management:
 * If called with the flag PTP_DP_GETDATA, this function will internally
 * allocate memory as much as necessary. The caller has to free the memory
 * returned in *data. If the function returns an error, it will free any
 * memory it might have allocated. The recvlen may be NULL. After the
 * function returns, *data will be initialized (valid memory pointer or NULL),
 * i.e. it is not necessary to initialize *data or *recvlen beforehand.
 */
uint16_t
ptp_transaction (PTPParams* params, PTPContainer* ptp,
		uint16_t flags, uint64_t sendlen,
		unsigned char **data, unsigned int *recvlen
) {
	PTPDataHandler	handler;
	uint16_t	ret;

	switch (flags & PTP_DP_DATA_MASK) {
	case PTP_DP_SENDDATA:
		if (!data)
			return PTP_ERROR_BADPARAM;
		CHECK_PTP_RC(ptp_init_send_memory_handler (&handler, *data, sendlen));
		break;
	case PTP_DP_GETDATA:
		if (!data)
			return PTP_ERROR_BADPARAM;
		*data = NULL;
		if (recvlen)
			*recvlen = 0;
		CHECK_PTP_RC(ptp_init_recv_memory_handler (&handler));
		break;
	default:break;
	}
	ret = ptp_transaction_new (params, ptp, flags, sendlen, &handler);
	switch (flags & PTP_DP_DATA_MASK) {
	case PTP_DP_SENDDATA:
		ptp_exit_send_memory_handler (&handler);
		break;
	case PTP_DP_GETDATA: {
		unsigned long len;
		ptp_exit_recv_memory_handler (&handler, data, &len);
		if (ret != PTP_RC_OK) {
			len = 0;
			free(*data);
			*data = NULL;
		}
		if (recvlen)
			*recvlen = len;
		break;
	}
	default:break;
	}
	return ret;
}


/**
 * PTP operation functions
 *
 * all ptp_ functions should take integer parameters
 * in host byte order!
 **/


/**
 * ptp_getdeviceinfo:
 * params:	PTPParams*
 *
 * Gets device info dataset and fills deviceinfo structure.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getdeviceinfo (PTPParams* params, PTPDeviceInfo* deviceinfo)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_GetDeviceInfo);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_DI(params, data, deviceinfo, size) ? PTP_RC_OK : PTP_ERROR_IO;
	free(data);
	return ret;
}

uint16_t
ptp_canon_eos_getdeviceinfo (PTPParams* params, PTPCanonEOSDeviceInfo*di)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetDeviceInfoEx);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_EOS_DI(params, data, di, size) ? PTP_RC_OK : PTP_ERROR_IO;
	free (data);
	return ret;
}

void ptp_canon_eos_free_deviceinfo (PTPCanonEOSDeviceInfo *di)
{
	if (!di) return;
	free (di->Events);
	free (di->DeviceProps);
	free (di->unk);
	memset(di, 0, sizeof(*di));
}

uint16_t
ptp_getstreaminfo (PTPParams *params, uint32_t streamid, PTPStreamInfo *si)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_GetStreamInfo, streamid);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_StreamInfo(params, data, si, size) ? PTP_RC_OK : PTP_ERROR_IO;
	free (data);
	return ret;
}

uint16_t
ptp_getstream (PTPParams *params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetStream);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_leica_getstreamdata (PTPParams *params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_LEICA_LEGetStreamData);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_canon_eos_905f (PTPParams* params, uint32_t x)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, 0x905f, x);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	free (data);
	return PTP_RC_OK;
}

#ifdef HAVE_LIBXML2
static int
traverse_tree (PTPParams *params, int depth, xmlNodePtr node)
{
	xmlNodePtr	next;
	xmlChar		*xchar;
	int		n;
	char 		*xx;


	if (!node) return 0;
	xx = malloc (depth * 4 + 1);
	memset (xx, ' ', depth*4);
	xx[depth*4] = 0;

	n = xmlChildElementCount (node);

	next = node;
	do {
		fprintf(stderr,"%snode %s\n", xx,next->name);
		fprintf(stderr,"%selements %d\n", xx,n);
		xchar = xmlNodeGetContent (next);
		fprintf(stderr,"%scontent %s\n", xx,xchar);
		traverse_tree (params, depth+1,xmlFirstElementChild (next));
	} while ((next = xmlNextElementSibling (next)));
	free (xx);
	return PTP_RC_OK;
}

static int
parse_9301_cmd_tree (PTPParams *params, xmlNodePtr node, PTPDeviceInfo *di)
{
	xmlNodePtr	next;
	int		cnt;

	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		cnt++;
		next = xmlNextElementSibling (next);
	}
	di->Operations_len = cnt;
	di->Operations = calloc (cnt,sizeof(di->Operations[0]));
	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		unsigned int p;

		sscanf((char*)next->name, "c%04x", &p);
		ptp_debug( params, "cmd %s / 0x%04x", next->name, p);
		di->Operations[cnt++] = p;
		next = xmlNextElementSibling (next);
	}
	return PTP_RC_OK;
}

static int
parse_9301_value (PTPParams *params, const char *str, uint16_t type, PTPPropValue *propval)
{
#define CASE(DTC, FMT, BITS) \
		case DTC: { \
			if (sscanf(str, FMT, &propval->BITS) != 1) { \
				ptp_debug( params, "could not parse " #BITS " %s", str); \
				return PTP_RC_GeneralError; \
			} \
			ptp_debug( params, "\t%d", propval->BITS); \
			break; \
		}

	switch (type) {
	CASE(PTP_DTC_UINT8, "%02hhx", u8)
	CASE(PTP_DTC_INT8, "%02hhx", i8)
	CASE(PTP_DTC_UINT16, "%04hx", u16)
	CASE(PTP_DTC_INT16, "%04hx", i16)
	CASE(PTP_DTC_UINT32, "%08x", u32)
	CASE(PTP_DTC_INT32, "%08x", i32)
	case PTP_DTC_STR: {
		int len;

		/* ascii ptp string, 1 byte length, little endian 16 bit chars */
		if (sscanf(str,"%02x", &len)) {
			int i;
			char *xstr = malloc(len+1);
			for (i=0;i<len;i++) {
				int xc;
				if (sscanf(str+2+i*4,"%04x", &xc)) {
					int cx;

					cx = ((xc>>8) & 0xff) | ((xc & 0xff) << 8);
					xstr[i] = cx;
				}
			}
			xstr[len] = '\0';
			ptp_debug( params, "\t%s", xstr);
			propval->str = xstr;
			break;
		}
		ptp_debug( params, "string %s not parseable!", str);
		return PTP_RC_GeneralError;
	}
	default:
		ptp_debug( params, "unhandled data type %d!", type);
		return PTP_RC_GeneralError;
	}
#undef CASE

	return PTP_RC_OK;
}

static int
parse_9301_propdesc (PTPParams *params, xmlNodePtr next, PTPDevicePropDesc *dpd)
{
	int type = -1;

	if (!next)
		return PTP_RC_GeneralError;

	ptp_debug (params, "parse_9301_propdesc");
	dpd->FormFlag	= PTP_DPFF_None;
	dpd->GetSet	= PTP_DPGS_Get;
	do {
		if (!strcmp((char*)next->name,"type")) {	/* propdesc.DataType */
			if (!sscanf((char*)xmlNodeGetContent (next), "%04x", &type)) {
				ptp_debug( params, "\ttype %s not parseable?",xmlNodeGetContent (next));
				return 0;
			}
			ptp_debug( params, "type 0x%x", type);
			dpd->DataType = type;
			continue;
		}
		if (!strcmp((char*)next->name,"attribute")) {	/* propdesc.GetSet */
			int attr;

			if (!sscanf((char*)xmlNodeGetContent (next), "%02x", &attr)) {
				ptp_debug( params, "\tattr %s not parseable",xmlNodeGetContent (next));
				return 0;
			}
			ptp_debug( params, "attribute 0x%x", attr);
			dpd->GetSet = attr;
			continue;
		}
		if (!strcmp((char*)next->name,"default")) {	/* propdesc.DefaultValue */
			ptp_debug( params, "default value");
			parse_9301_value (params, (char*)xmlNodeGetContent (next), type, &dpd->DefaultValue);
			continue;
		}
		if (!strcmp((char*)next->name,"value")) {	/* propdesc.CurrentValue */
			ptp_debug( params, "current value");
			parse_9301_value (params, (char*)xmlNodeGetContent (next), type, &dpd->CurrentValue);
			continue;
		}
		if (!strcmp((char*)next->name,"enum")) {	/* propdesc.FORM.Enum */
			int n,i;
			char *s;

			ptp_debug( params, "enum");
			dpd->FormFlag = PTP_DPFF_Enumeration;
			s = (char*)xmlNodeGetContent (next);
			n = 0;
			do {
				s = strchr(s,' ');
				if (s) s++;
				n++;
			} while (s);
			dpd->FORM.Enum.NumberOfValues = n;
			dpd->FORM.Enum.SupportedValue = calloc (n , sizeof(PTPPropValue));
			s = (char*)xmlNodeGetContent (next);
			i = 0;
			do {
				parse_9301_value (params, s, type, &dpd->FORM.Enum.SupportedValue[i]); /* should turn ' ' into \0? */
				i++;
				s = strchr(s,' ');
				if (s) s++;
			} while (s && (i<n));
			continue;
		}
		if (!strcmp((char*)next->name,"range")) {	/* propdesc.FORM.Enum */
			char *s = (char*)xmlNodeGetContent (next);
			dpd->FormFlag = PTP_DPFF_Range;
			ptp_debug( params, "range");
			parse_9301_value (params, s, type, &dpd->FORM.Range.MinValue); /* should turn ' ' into \0? */
			s = strchr(s,' ');
			if (!s) continue;
			s++;
			parse_9301_value (params, s, type, &dpd->FORM.Range.MaxValue); /* should turn ' ' into \0? */
			s = strchr(s,' ');
			if (!s) continue;
			s++;
			parse_9301_value (params, s, type, &dpd->FORM.Range.StepSize); /* should turn ' ' into \0? */

			continue;
		}
		ptp_debug (params, "\tpropdescvar: %s", next->name);
		traverse_tree (params, 3, next);
	} while ((next = xmlNextElementSibling (next)));
	return PTP_RC_OK;
}

static int
parse_9301_prop_tree (PTPParams *params, xmlNodePtr node, PTPDeviceInfo *di)
{
	xmlNodePtr	next;
	int		cnt;

	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		cnt++;
		next = xmlNextElementSibling (next);
	}

	di->DeviceProps_len = cnt;
	di->DeviceProps = calloc (cnt,sizeof(di->DeviceProps[0]));
	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		unsigned int p;
		PTPDevicePropDesc	dpd;

		sscanf((char*)next->name, "p%04x", &p);
		ptp_debug( params, "prop %s / 0x%04x", next->name, p);
		parse_9301_propdesc (params, xmlFirstElementChild (next), &dpd);
		dpd.DevicePropCode = p;
		dpd.timestamp = time(NULL);
		di->DeviceProps[cnt++] = p;

		/* add to cache of device propdesc */
		PTPDevicePropDesc* dpd_in_cache = ptp_find_dpd_in_cache(params, p);
		if (!dpd_in_cache)
			array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
		else
			ptp_free_devicepropdesc (dpd_in_cache);
		move(*dpd_in_cache, dpd);

		next = xmlNextElementSibling (next);
	}
	return PTP_RC_OK;
}

static int
parse_9301_event_tree (PTPParams *params, xmlNodePtr node, PTPDeviceInfo *di)
{
	xmlNodePtr	next;
	int		cnt;

	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		cnt++;
		next = xmlNextElementSibling (next);
	}
	di->Events_len = cnt;
	di->Events = calloc (cnt,sizeof(di->Events[0]));
	cnt = 0;
	next = xmlFirstElementChild (node);
	while (next) {
		unsigned int p;

		sscanf((char*)next->name, "e%04x", &p);
		ptp_debug( params, "event %s / 0x%04x", next->name, p);
		di->Events[cnt++] = p;
		next = xmlNextElementSibling (next);
	}
	return PTP_RC_OK;
}

static int
parse_9301_tree (PTPParams *params, xmlNodePtr node, PTPDeviceInfo *di)
{
	xmlNodePtr	next;

	next = xmlFirstElementChild (node);
	while (next) {
		if (!strcmp ((char*)next->name, "cmd")) {
			parse_9301_cmd_tree (params, next, di);
			next = xmlNextElementSibling (next);
			continue;
		}
		if (!strcmp ((char*)next->name, "prop")) {
			parse_9301_prop_tree (params, next, di);
			next = xmlNextElementSibling (next);
			continue;
		}
		if (!strcmp ((char*)next->name, "event")) {
			parse_9301_event_tree (params, next, di);
			next = xmlNextElementSibling (next);
			continue;
		}
		fprintf (stderr,"9301: unhandled type %s\n", next->name);
		next = xmlNextElementSibling (next);
	}
	/*traverse_tree (0, node);*/
	return PTP_RC_OK;
}
#endif

uint16_t
ptp_olympus_omd_capture (PTPParams* params)
{
	PTPContainer	ptp;
	unsigned int	size = 0;
	unsigned char	*data = NULL;

/* these two trigger the capture ... one might be "shutter down", the other "shutter up"? */
	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OMD_Capture, 0x3); // initiate capture
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OMD_Capture, 0x6); // initiate capture
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));

	usleep(500);

/* this only fetches changed props */
	PTP_CNT_INIT(ptp, 0x9486); /* query changed properties */
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	free (data);
	return PTP_RC_OK;
}
/**
 * ptp_olympus_bulbstart:
 * params:	PTPParams*
 *
 * Starts Olympus Bulb capture.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_olympus_omd_bulbstart (PTPParams* params)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OMD_Capture, 0x3); // initiate capture
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if ((ptp.Nparam >= 1) && ((ptp.Param1 & 0x7000) == 0x2000))
		return ptp.Param1;
	return PTP_RC_OK;
}


/**
 * ptp_olympus_bulbend:
 * params:	PTPParams*
 *
 * Stops Olympus Bulb capture.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_olympus_omd_bulbend (PTPParams* params)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OMD_Capture, 0x6); // initiate capture
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if ((ptp.Nparam >= 1) && ((ptp.Param1 & 0x7000) == 0x2000))
		return ptp.Param1;
	return PTP_RC_OK;
}

uint16_t
ptp_panasonic_liveview_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_LiveviewImage);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_liveview_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCamViewFrame);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

static uint16_t /* TODO: why is this return value ignored at all call sights? */
ptp_sigma_fp_parse_ifdlist (PTPParams* params, unsigned char *data, unsigned int size)
{
	uint32_t	datalen, entrycount, offset;
	unsigned int	i;

	if (size < 5) {
		ptp_debug (params, "size %d is less than 5", size);
		return PTP_RC_GeneralError;
	}
	datalen = dtoh32a(data);
	if (datalen != size-4-1) {
		ptp_debug (params, "size %d is not specified size %d", size-4-1, datalen);
		return PTP_RC_GeneralError;
	}

	entrycount = dtoh32a(data+4);
	offset = 8;
	if (8 + 12*entrycount > size) {
		ptp_debug (params, "count %d entries do not fit size %d", entrycount, size);
		return PTP_RC_GeneralError;
	}
	for (i = 0; i<entrycount; i++) {
		uint16_t tag = dtoh16a(data+offset+i*12), type = dtoh16a(data+offset+i*12+2);
		uint32_t count = dtoh32a(data+offset+i*12+4), value = dtoh32a(data+offset+i*12+8);
		/* word tag, word type, dword elements, value */
		ptp_debug (params, "entry %d: tag=0x%04x, type=0x%04x, elements=%d, value=0x%08x", i, tag, type, count, value);
		switch (dtoh16a(data+offset+i*12+2)) {
		case 0x2: /* ASCII */
			if (count > 4) {
				ptp_debug (params, "ascii: %s", &data[dtoh32a(data+offset+i*12+8)]);
			} else {
				ptp_debug (params, "ascii: %s", data+offset+i*12+8);
			}
			break;
		case 0xb: /* 32bit ieee float */
			ptp_debug (params, "float: %f", *(float*)(data+offset+i*12+8));
			break;
		}
	}
	return PTP_RC_OK;
#if 0
0000  5b 00 00 00 02 00 35 90-02 00 00 00

4a 00 00 00  [.....5.....J...
0010  04 00 00 00 01 00 02 00-09 00 00 00 3c 00 00 00  ............<...
0020  02 00 02 00 09 00 00 00-45 00 00 00 03 00 02 00  ........E.......
0030  04 00 00 00 56 38 34 00-05 00 0b 00 01 00 00 00  ....V84.........
0040  52 b8 9e 3f 00 00 00 00-53 49 47 4d 41 20 66 70  R..?....SIGMA fp
0050  00 39 31 34 30 33 37 32-37 00 b3                 .91403727..

#endif
}

uint16_t
ptp_sigma_fp_9035 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCameraInfo);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size));
	ptp_sigma_fp_parse_ifdlist (params, *data, *size);
	free(*data);
	return PTP_RC_OK;
}

uint16_t
ptp_sigma_fp_getcamcansetinfo5 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCamCanSetInfo5);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size));
	ptp_sigma_fp_parse_ifdlist (params, *data, *size);
	free(*data);
	return PTP_RC_OK;
}

uint16_t
ptp_sigma_fp_getcamdatagroupfocus (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCamDataGroupFocus);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size));
	ptp_sigma_fp_parse_ifdlist (params, *data, *size);
	free(*data);
	return PTP_RC_OK;
}

uint16_t
ptp_sigma_fp_getcamdatagroupmovie (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCamDataGroupMovie);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size));
	ptp_sigma_fp_parse_ifdlist (params, *data, *size);
	free(*data);
	return PTP_RC_OK;
}

uint16_t
ptp_sigma_fp_getdatagroup1 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup1);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getdatagroup2 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup2);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getdatagroup3 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup3);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getdatagroup4 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup4);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getdatagroup5 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup5);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getdatagroup6 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetDataGroup6);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_setdatagroup1 (PTPParams* params, unsigned char *data, unsigned int size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_SetDataGroup1);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, 0);
}


uint16_t
ptp_sigma_fp_getcapturestatus (PTPParams* params, unsigned int p1, SIGMAFP_CaptureStatus*status)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCaptureStatus, p1);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 7) {
		ptp_debug (params, "size %d is smaller than expected 7", size);
		goto error;
	}
	if (data[0] != 6) {
		ptp_debug (params, "byte size %d is smaller than expected 6", data[0]);
		goto error;
	}
	status->imageid		= data[1];
	status->imagedbhead	= data[2];
	status->imagedbtail	= data[3];
	status->status		= dtoh16a(&data[4]);
	status->destination	= data[5];
	/* data[6] is the checksum (sum of bytes 0->5 */
	ptp_debug (params, "ptp_sigma_fp_getcapturestatus: imageid 0x%02x, imagedbhead 0x%02x, imagedbtail 0x%02x, status 0x%04x, dest 0x%02x",
		status->imageid, status->imagedbhead, status->imagedbtail, status->status, status->destination
	);
	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_sigma_fp_getcamstatus2 (PTPParams* params, uint32_t canset, uint32_t datagroup, uint32_t other, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetCamStatus2, canset, datagroup, other);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_getpictfileinfo2 (PTPParams* params, SIGMAFP_PictFileInfo2Ex *pictinfo)
{
	PTPContainer	ptp;
	unsigned int	off;
	unsigned char	*data = NULL;
	unsigned int	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetPictFileInfo2);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 60) {
		ptp_debug (params, "size %d is smaller than expected 60", size);
		goto error;
	}
	if (dtoh32a(data) != 56) {
		ptp_debug (params, "dword size %d is smaller than expected 56", dtoh32a(data));
		goto error;
	}
	memset(pictinfo, 0, sizeof(*pictinfo));
	pictinfo->fileaddress = dtoh32a(&data[12]);	/* perhaps ? */
	pictinfo->filesize = dtoh32a(&data[16]);
	strncpy(pictinfo->fileext, (char*)data+28, 4);
	pictinfo->width = dtoh16a(&data[32]);
	pictinfo->height = dtoh16a(&data[34]);
	off = dtoh32a(&data[20]);
	if (off > size) {
		ptp_debug (params, "off %d is larger than size %d", off, size);
		goto error;
	}
	strncpy(pictinfo->path, (char*)data+off, 9);
	off = dtoh32a(&data[24]);
	if (off > size) {
		ptp_debug (params, "off %d is larger than size %d", off, size);
		goto error;
	}
	strncpy(pictinfo->name, (char*)data+off, 9);

#if 0
uint16_t        pictureformat;	/* ? */
char            fileext[4];	/* 28,29,30,31 */
uint16_t        width;		/* 32, 33 */
uint16_t        height;		/* 34, 35 */
char            path[128];	/* offset 20,21,22,23 => 36 */
char            name[128];	/* offset 24,25,26,27 => 45 */
uint32_t        filesize;	/* 16,17,18,19 */
uint32_t        fileaddress;	/* 12,13,14,15 */


38 00 00 00

01 00 00 00 0c 00 00 00 80 05 00 57 49 24 63 00  ...........WI$c.
24 00 00 00 2d 00 00 00 4a 50 47 00 70 17 a0 0f  $...-...JPG.p...
31 30 30 53 49 47 4d 41 00 53 44 49 4d 30 30 30  100SIGMA.SDIM000
31 2e 4a 50 47 00 00 00                          1.JPG...
#endif
	free (data);
	return PTP_RC_OK;
error:
	free (data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_sigma_fp_getbigpartialpictfile (PTPParams* params, unsigned int p1, unsigned int off, unsigned int insize, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_GetBigPartialPictFile, p1, off, insize);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_sigma_fp_snap (PTPParams* params, unsigned int mode, unsigned int amount)
{
	PTPContainer	ptp;
	unsigned char	data[4];

	data[0] = 0x02;			/* num data bytes */
	data[1] = mode;
	data[2] = amount;
	data[3] = 0x02+mode+amount;	/* checksum, just add everything */

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_Snap);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, 0);
}

uint16_t
ptp_sigma_fp_clearimagedbsingle (PTPParams* params, uint32_t id)
{
	PTPContainer	ptp;
	unsigned char	data[8] = {0};

	PTP_CNT_INIT(ptp, PTP_OC_SIGMA_FP_ClearImageDBSingle, id);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, 0);
}

uint16_t
ptp_olympus_init_pc_mode (PTPParams* params)
{
	uint16_t		ret;
	PTPPropValue	propval;
	PTPContainer		event;
	int			i;

	ptp_debug (params,"PTP: (Olympus Init) switching to PC mode...");

	propval.u16 = 1;
	ret = ptp_setdevicepropvalue (params, 0xD052, &propval, PTP_DTC_UINT16);
	usleep(100000);

	for(i = 0; i < 2; i++) {
		ptp_debug (params,"PTP: (Olympus Init) checking events...");
		/* Just busy loop until the camera is ready again. */
		ptp_check_event (params);
		if (ptp_get_one_event(params, &event)) break;
		usleep(100000);
	}

/*
 * 9489 code: sends a list of PTP device properties supported apparently? on E-M1.
 * F4 00 00 00	count
02 D0 03 D0 04 D0 05 D0 06 D0 07 D0 08 D0 09 D0 0C D0 0D D0 0E D0 0F D0 10 D0 11 D0 13 D0 14 D0 18 D0 1A D0 1B D0 1C D0 1D D0 1E D0 1F D0 20 D0 21 D0 22 D0 23 D0 24 D0 25 D0 26 D0 27 D0 28 D0 29 D0 2A D0 2B D0 2C D0 2D D0 2E D0 2F D0 30 D0 31 D0 32 D0 33 D0 34 D0 35 D0 36 D0 37 D0 38 D0 39 D0 3A D0 3B D0 3C D0 3D D0 3E D0 3F D0 40 D0 41 D0 42 D0 43 D0 44 D0 45 D0 46 D0 47 D0 48 D0 49 D0 4A D0 4B D0 4C D0 4D D0 4E D0 4F D0 50 D0 51 D0 52 D0 58 D0 59 D0 5F D0 60 D0 61 D0 62 D0 64 D0 65 D0 66 D0 68 D0 69 D0 70 D0 73 D0 67 D0 5A D0 5B D0 63 D0 6A D0 6B D0 6C D0 71 D0 72 D0 7A D0 7B D0 7C D0 7D D0 7F D0 80 D0 81 D0 82 D0 86 D0 87 D0 8B D0 8C D0 8E D0 8F D0 97 D0 9F D0 C4 D0 C5 D0 A2 D0 A3 D0 A4 D0 A6 D0 A7 D0 A8 D0 A9 D0 AA D0 AB D0 AC D0 AD D0 AE D0 B2 D0 B3 D0 B4 D0 B5 D0 B6 D0 B7 D0 B8 D0 B9 D0 BA D0 BC D0 BD D0 BE D0 BF D0 C0 D0 C6 D0 C7 D0 C8 D0 C9 D0 CB D0 CC D0 CD D0 CE D0 CF D0 D0 D0 D1 D0 D2 D0 D3 D0 D4 D0 D5 D0 D6 D0 D7 D0 D8 D0 D9 D0 DA D0 DB D0 DC D0 DD D0 DE D0 E2 D0 E3 D0 E4 D0 E5 D0 E6 D0 E7 D0 E8 D0 E9 D0 EA D0 EC D0 EF D0 F0 D0 F1 D0 F2 D0 F3 D0 F4 D0 F5 D0 F6 D0 F7 D0 F8 D0 F9 D0 FA D0 FB D0 FC D0 FD D0 FE D0 FF D0 00 D1 01 D1 02 D1 03 D1 04 D1 05 D1 06 D1 07 D1 08 D1 09 D1 0A D1 0B D1 0C D1 0D D1 0E D1 0F D1 10 D1 11 D1 12 D1 13 D1 14 D1 15 D1 16 D1 17 D1 18 D1 19 D1 1A D1 1B D1 1C D1 1D D1 1E D1 1F D1 20 D1 51 D1 52 D1 5A D1 24 D1 25 D1 26 D1 27 D1 28 D1 2D D1 2E D1 2F D1 30 D1 31 D1 34 D1 35 D1 36 D1 37 D1 38 D1 39 D1 3A D1
 *
 * 9486: queries something. gets 00 00 00 00 ... or list of devicepropdesc in standard ptp propdesc format.
 * could be some form of "properties changed" query perhaps? (32bit count in front)
 * might only monitor/return properties set by 9489?
 *
 * 948a: seems also be some kind of polling function, returns 32bit 0 if nothing is there. similar to above?
 *       returns properties sent by 94b8.
 *
 * 948b: also sends a list of ptp devprops:
 * 11 00 00 00 53 D0 54 D0 55 D0 56 D0 57 D0 6D D0 5C D0 5D D0 5E D0 74 D0 75 D0 83 D0 84 D0 85 D0 ED D0 79 D0 E1 D0
 * Events: c008: 21 D1 00 00 0F 00 00 00 01 00 00 00
 */
	//ptp_debug (params,"PTP: (Olympus Init) getting response...");
	//gp_port_set_timeout (camera->port, timeout);
	//ret=ptp_transaction(params, &ptp, PTP_DP_RESPONSEONLY, size, &data, NULL);
	//free(data);
	return ret;
}

uint16_t
ptp_olympus_liveview_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;
	uint32_t 	param1 = 1;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_GetLiveViewImage, param1);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_olympus_sdram_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OMD_GetImage);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_panasonic_9401 (PTPParams* params, uint32_t param1)
{
	PTPContainer	ptp;
	unsigned int	*size = 0;
	unsigned char	*data = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_9401, param1);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, size));
	free(data);
	return PTP_RC_OK;
}

/* liveview format query
 * 0d800012 - get imageformats
 * 0d800011 - get current imageformat ?
 */
uint16_t
ptp_panasonic_9414_0d800012 (PTPParams* params, PanasonicLiveViewSize **liveviewsizes, unsigned int *nrofliveviewsizes)
{
	PTPContainer	ptp;
	unsigned int	i;
	unsigned int	size = 0;
	unsigned char	*data = NULL;
	uint32_t	blobsize;
	uint16_t	count;
	uint16_t	structsize;

	*nrofliveviewsizes = 0;
	*liveviewsizes = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_GetLiveViewParameters, 0x0d800012);
	CHECK_PTP_RC (ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
/* return data with the GC9 is:

12 00 80 0d
1c 00 00 00
03 00           count
08 00           structsize
e0 01 80 02 20 00 1e 00
d0 02 c0 03 5e 01 1e 00
c0 03 00 05 bc 02 1e 00

 */
	if (size < 8)
		goto error;

	blobsize = dtoh32a(data+4);
	if (size - 8 < blobsize) {
		ptp_debug (params, "blobsize expected %d, but size is only %d", blobsize, size - 8);
		goto error;
	}
	if (blobsize < 4) {
		ptp_debug (params, "blobsize expected at least 4, but is only %d", blobsize);
		goto error;
	}
	count		= dtoh16a (data+8);
	structsize	= dtoh16a (data+10);
	if (structsize != 8) {
		ptp_debug (params, "structsize expected 8, but is %d", structsize);
		goto error;
	}
	if (count * structsize > blobsize) {
		ptp_debug (params, "%d * %d = %d is larger than %d", count, structsize, count * structsize, blobsize);
		goto error;
	}

	*liveviewsizes = calloc (count, sizeof(PanasonicLiveViewSize));
	if (!*liveviewsizes)
		goto error;
	for (i = 0;i < count; i++) {
		(*liveviewsizes)[i].height	= dtoh16a (data + 12 + i*structsize);
		(*liveviewsizes)[i].width	= dtoh16a (data + 12 + 2 + i*structsize);
		(*liveviewsizes)[i].x		= dtoh16a (data + 12 + 4 + i*structsize);
		(*liveviewsizes)[i].freq	= dtoh16a (data + 12 + 6 + i*structsize);
	}
	*nrofliveviewsizes = count;
	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_panasonic_9414_0d800011 (PTPParams* params, PanasonicLiveViewSize *liveviewsize)
{
	PTPContainer	ptp;
	unsigned int	size = 0;
	unsigned char	*data = NULL;
	uint32_t	blobsize;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_GetLiveViewParameters, 0x0d800011);
	CHECK_PTP_RC (ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
/* data is:

11 00 80 0d
08 00 00 00

e0 01 80 02
00 00
1e 00

 */
	if (size < 8)
		goto error;

	blobsize = dtoh32a(data+4);
	if (size - 8 < blobsize) {
		ptp_debug (params, "blobsize expected %d, but size is only %d", blobsize, size - 8);
		goto error;
	}
	if (blobsize < 8) {
		ptp_debug (params, "blobsize expected at least 8, but is only %d", blobsize);
		goto error;
	}
	liveviewsize->height	= dtoh16a (data + 8 + 0);
	liveviewsize->width	= dtoh16a (data + 8 + 2);
	liveviewsize->x		= dtoh16a (data + 8 + 4);
	liveviewsize->freq	= dtoh16a (data + 8 + 6);
	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_panasonic_9415 (PTPParams* params, PanasonicLiveViewSize *liveviewsize)
{
	PTPContainer	ptp;
	unsigned char	data[16] = {0};

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_SetLiveViewParameters, 0x0d800011);

	htod32a(data+0 , 0x0d800011);
	htod32a(data+4 , 8);
	htod16a(data+8 , liveviewsize->height);
	htod16a(data+10, liveviewsize->width);
	htod16a(data+12, liveviewsize->x);
	htod16a(data+14, liveviewsize->freq);

	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, 0);
}

uint16_t
ptp_panasonic_setdeviceproperty (PTPParams* params, uint32_t propcode,
			unsigned char *value, uint16_t valuesize)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint32_t 	size = 4 + 4 + valuesize;

	data = calloc(size, sizeof(unsigned char));

	htod32a(data, propcode); /* memcpy(data, &propcode, 4); */
	htod16a(&data[4], valuesize); /* memcpy(&data[4], &valuesize, 2); */

	memcpy(&data[8], value, valuesize);	/* perhaps check if one of the DPV packagers work? */

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_SetProperty, propcode);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_panasonic_getdevicepropertysize (PTPParams *params, uint32_t propcode)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_9107, propcode, 0, 0);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 4) goto error;
	uint32_t headerLength 		= dtoh32a( (data) + 4 );
	if (size < 4 + 6 * 4) goto error;
	uint32_t propertyCode 		= dtoh32a( (data) + 4 + 6 * 4 );
	if (size < headerLength * 4 + 2 * 4) goto error;

	ptp_debug(params, "header: %u, code: %u\n", headerLength, propertyCode);

	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_panasonic_manualfocusdrive (PTPParams* params, uint16_t mode)
{
	PTPContainer	ptp;
	unsigned char	data[10];
	uint32_t 	propcode = 0x03010011;
	uint32_t 	type = 2;

	htod32a(data, propcode);	/* memcpy(data, &propcode, 4); */
	htod32a(&data[4], type);	/* memcpy(&data[4], &type, 4); */
	htod16a(&data[8], mode);	/* memcpy(&data[8], &mode, 2); */

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_ManualFocusDrive, propcode);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, NULL);
}

uint16_t
ptp_panasonic_recordmode (PTPParams* params, uint16_t mode)
{
	PTPContainer	ptp;
	unsigned char	data[10];
	uint32_t 	propcode = 0x06000011;
	uint32_t 	type = 2;

	htod32a(data, propcode);	/* memcpy(data, &propcode, 4); */
	htod32a(&data[4], type);	/* memcpy(&data[4], &type, 4); */
	htod16a(&data[8], mode);	/* memcpy(&data[8], &mode, 2); */

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_9409, propcode);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, NULL);
}

uint16_t
ptp_panasonic_startrecording (PTPParams* params)
{
	PTPContainer	ptp;
	uint32_t	propcode = 0x07000011;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_MovieRecControl, propcode);
	return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
}

uint16_t
ptp_panasonic_stoprecording (PTPParams* params)
{
	PTPContainer	ptp;
	uint32_t 	propcode = 0x07000012;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_MovieRecControl, propcode);
	return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
}

uint16_t
ptp_panasonic_getcapturetarget (PTPParams* params, uint16_t *target)
{
	PTPContainer	ptp;
	unsigned char	*data;
	unsigned int	size;

	*target = 0;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_GetCaptureTarget, 0x08000090);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (size < 10) goto error;
	if (dtoh32a(data) != 0x08000091) goto error;
	if (dtoh32a(data+4) != 2) goto error;
	*target = dtoh16a(data+8);
	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_panasonic_setcapturetarget (PTPParams* params, uint16_t mode) // mode == 1 == RAM, mode == 0 == SD, 2 = BOTH
{
	PTPContainer	ptp;
	unsigned char	data[10];

	htod32a(data, 	  0x08000091);	/* capturetarget */
	htod32a(&data[4], 2); 		/* size */
	htod16a(&data[8], mode);

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_SetCaptureTarget, 0);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, sizeof(data), (unsigned char**)&data, NULL);
}

uint16_t
ptp_panasonic_getdevicepropertydesc (PTPParams *params, uint32_t propcode, uint16_t valuesize, uint32_t *currentValue, uint32_t **propertyValueList, uint32_t *propertyValueListLength)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int 	size = 0;
	uint16_t	ret = PTP_RC_OK;
	uint32_t	headerLength;
	uint32_t	propertyCode;
	unsigned int	off = 0;
	unsigned int	i;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_ListProperty, propcode, 0, 0);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (size < 8)
		return PTP_RC_GeneralError;

	ptp_debug (params, "ptp_panasonic_getdevicepropertydesc 0x%08x", propcode);
	while (off < size) {
		if (off >= size-8) break;
		ptp_debug (params, "propcode 0x%08x, size %d", dtoh32a(data+off), dtoh32a(data+off+4));
		off += dtoh32a(data+off+4)+8;
	}

	headerLength = dtoh32a( (data) + 4 );
	if (size < 4 + 6 * 4)
		return PTP_RC_GeneralError;

	propertyCode = dtoh32a( (data) + 4 + 6 * 4 );
	if (size < headerLength * 4 + 2 * 4)
		return PTP_RC_GeneralError;

	if(valuesize == 2) {
		*currentValue 		= (uint32_t) dtoh16a( (data) + headerLength * 4 + 2 * 4 );
	} else if(valuesize == 4) {
		*currentValue 		= dtoh32a( (data) + headerLength * 4 + 2 * 4 );
	} else {
		ptp_debug (params, "unexpected valuesize %d", valuesize);
		return PTP_RC_GeneralError;
	}
	/* Marcus: where is 4*headerLenght coming from ? */
	if (size < headerLength * 4 + 2 * 4 + valuesize) return PTP_RC_GeneralError;
	*propertyValueListLength 		= dtoh32a( (data) + headerLength * 4 + 2 * 4 + valuesize);

	ptp_debug(params, "header: %u, code: 0x%x, value: %u, count: %u", headerLength, propertyCode, *currentValue, *propertyValueListLength);

	if (size < headerLength * 4 + 3 * 4 + valuesize + (*propertyValueListLength) * valuesize) {
		ptp_debug (params, "size %d vs expected size %d", size, headerLength * 4 + 3 * 4 + valuesize + (*propertyValueListLength) * valuesize);
		return PTP_RC_GeneralError;
	}

	*propertyValueList = calloc(*propertyValueListLength, sizeof(uint32_t));

	for(i = 0; i < *propertyValueListLength; i++) {
		if(valuesize == 2) {
			(*propertyValueList)[i] = (uint32_t) dtoh16a( (data) + headerLength * 4 + 3 * 4 + valuesize + i * valuesize);
		} else if(valuesize == 4) {
			(*propertyValueList)[i] = dtoh32a( (data) + headerLength * 4 + 3 * 4 + valuesize + i * valuesize);
		}
		//printf("Property: %lu\n", (*propertyValueList)[i]);
	}

	free (data);
	return ret;
}

uint16_t
ptp_panasonic_getrecordingstatus (PTPParams *params, uint32_t propcode, uint16_t *valuesize, uint32_t *currentValue)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int 	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_PollEvents, propcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if(size < 8) goto error;
	*valuesize = dtoh32a( (data + 4) );

	if(size < 8u + (*valuesize)) goto error;
	if(*valuesize == 4) {
		*currentValue = dtoh32a( (data + 8) );
	} else if(*valuesize == 2) {
		*currentValue = (uint32_t) dtoh16a( (data + 8) );
	} else {
		goto error;
	}
	//printf("ptp_panasonic_getdeviceproperty: size: %lu, valuesize: %d, currentValue: %lu\n", size, *valuesize, *currentValue);

	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_panasonic_getdeviceproperty (PTPParams *params, uint32_t propcode, uint16_t *valuesize, uint32_t *currentValue)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int 	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_PANASONIC_GetProperty, propcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if(size < 8) goto error;
	*valuesize = dtoh32a( (data + 4) );

	if(size < 8u + (*valuesize)) goto error;
	if(*valuesize == 4) {
		*currentValue = dtoh32a( (data + 8) );
	} else if(*valuesize == 2) {
		*currentValue = (uint32_t) dtoh16a( (data + 8) );
	} else {
		goto error;
	}
	//printf("ptp_panasonic_getdeviceproperty: size: %lu, valuesize: %d, currentValue: %lu\n", size, *valuesize, *currentValue);

	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

#ifdef HAVE_LIBXML2
static uint16_t
ptp_olympus_parse_output_xml(PTPParams* params, char*data, int len, xmlNodePtr *code)
{
	xmlDocPtr	docin;
	xmlNodePtr	docroot, output, next;
	int 		result, xcode;

	*code = NULL;

	docin = xmlReadMemory ((char*)data, len, "http://gphoto.org/", "utf-8", 0);
	if (!docin) return PTP_RC_GeneralError;
	docroot = xmlDocGetRootElement (docin);
	if (!docroot) {
		xmlFreeDoc (docin);
		return PTP_RC_GeneralError;
	}

	if (strcmp((char*)docroot->name,"x3c")) {
		ptp_debug (params, "olympus: docroot is not x3c, but %s", docroot->name);
		xmlFreeDoc (docin);
		return PTP_RC_GeneralError;
	}
	if (xmlChildElementCount(docroot) != 1) {
		ptp_debug (params, "olympus: x3c: expected 1 child, got %ld", xmlChildElementCount(docroot));
		xmlFreeDoc (docin);
		return PTP_RC_GeneralError;
	}
	output = xmlFirstElementChild (docroot);
	if (strcmp((char*)output->name, "output") != 0) {
		ptp_debug (params, "olympus: x3c node: expected child 'output', but got %s", (char*)output->name);
		xmlFreeDoc (docin);
		return PTP_RC_GeneralError;
	}
	next = xmlFirstElementChild (output);

	result = PTP_RC_GeneralError;

	while (next) {
		if (!strcmp((char*)next->name,"result")) {
			xmlChar	 *xchar;

			xchar = xmlNodeGetContent (next);
			if (!sscanf((char*)xchar,"%04x",&result))
				ptp_debug (params, "failed scanning result from %s", xchar);
			ptp_debug (params,  "ptp result is 0x%04x", result);
			next = xmlNextElementSibling (next);
			continue;
		}
		if (sscanf((char*)next->name,"c%x", &xcode)) {
			ptp_debug (params,  "ptp code node found %s", (char*)next->name);
			*code = next;
			next = xmlNextElementSibling (next);
			continue;
		}
		ptp_debug (params, "unhandled node %s", (char*)next->name);
		next = xmlNextElementSibling (next);
	}

	if (result != PTP_RC_OK) {
		*code = NULL;
		xmlFreeDoc (docin);
	}
	return result;
}
#endif

uint16_t
ptp_olympus_getdeviceinfo (PTPParams* params, PTPDeviceInfo *di)
{
#ifdef HAVE_LIBXML2
	PTPContainer	ptp;
	uint16_t 	ret;
	unsigned char	*data = NULL;
	unsigned int	size;
	xmlNodePtr	code;

	memset (di, 0, sizeof(PTPDeviceInfo));

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_GetDeviceInfo);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_olympus_parse_output_xml(params,(char*)data,size,&code);
	free(data);
	if (ret != PTP_RC_OK)
		return ret;

	ret = parse_9301_tree (params, code, di);

	xmlFreeDoc(code->doc);
	return ret;
#else
	return PTP_RC_GeneralError;
#endif
}

uint16_t
ptp_olympus_opensession (PTPParams* params, unsigned char**data, unsigned int *len)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_OpenSession);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, len);
}

uint16_t
ptp_olympus_getcameraid (PTPParams* params, unsigned char**data, unsigned int *len)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_OLYMPUS_GetCameraID);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, len);
}

/**
 * ptp_generic_no_data:
 * params:	PTPParams*
 * 		code	PTP OP Code
 * 		n_param	count of parameters
 *		... variable argument list ...
 *
 * Emits a generic PTP command without any data transfer.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_generic_no_data (PTPParams* params, uint16_t code, unsigned int n_param, ...)
{
	PTPContainer	ptp;
	va_list		args;
	unsigned int	i;

	if( n_param > 5 )
		return PTP_ERROR_BADPARAM;

	memset(&ptp, 0, sizeof(ptp));
	ptp.Code=code;
	ptp.Nparam=n_param;

	va_start(args, n_param);
	for( i=0; i<n_param; ++i )
		(&ptp.Param1)[i] = va_arg(args, uint32_t);
	va_end(args);

	return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
}

/**
 * ptp_opensession:
 * params:	PTPParams*
 * 		session			- session number
 *
 * Establishes a new session.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_opensession (PTPParams* params, uint32_t session)
{
	PTPContainer	ptp;
	uint16_t	ret;

	ptp_debug(params,"PTP: Opening session");

	/* SessonID field of the operation dataset should always
	   be set to 0 for OpenSession request! */
	params->session_id=0x00000000;
	/* TransactionID should be set to 0 also! */
	params->transaction_id=0x0000000;
	/* zero out response packet buffer */
	params->response_packet = NULL;
	params->response_packet_size = 0;
	/* no split headers */
	params->split_header_data = 0;

	PTP_CNT_INIT(ptp, PTP_OC_OpenSession, session);
	ret=ptp_transaction_new(params, &ptp, PTP_DP_NODATA, 0, NULL);
	/* TODO: check for error */
	/* now set the global session id to current session number */
	params->session_id=session;
	return ret;
}

void
ptp_free_propvalue(uint16_t dt, PTPPropValue* prop)
{
	if (dt == PTP_DTC_STR)
		free(prop->str);
	else if ((dt & 0xFFF0) == PTP_DTC_ARRAY_MASK)
		free(prop->a.v);
}

void
ptp_free_devicepropdesc(PTPDevicePropDesc* dpd)
{
	uint16_t i;

	ptp_free_propvalue (dpd->DataType, &dpd->DefaultValue);
	ptp_free_propvalue (dpd->DataType, &dpd->CurrentValue);
	switch (dpd->FormFlag) {
	case PTP_DPFF_Range:
		ptp_free_propvalue (dpd->DataType, &dpd->FORM.Range.MinValue);
		ptp_free_propvalue (dpd->DataType, &dpd->FORM.Range.MaxValue);
		ptp_free_propvalue (dpd->DataType, &dpd->FORM.Range.StepSize);
		break;
	case PTP_DPFF_Enumeration:
		if (dpd->FORM.Enum.SupportedValue) {
			for (i=0;i<dpd->FORM.Enum.NumberOfValues;i++)
				ptp_free_propvalue (dpd->DataType, dpd->FORM.Enum.SupportedValue+i);
			free (dpd->FORM.Enum.SupportedValue);
		}
	}
	dpd->DataType = PTP_DTC_UNDEF;
	dpd->FormFlag = PTP_DPFF_None;
}


void
ptp_free_objectpropdesc(PTPObjectPropDesc* opd)
{
	uint16_t i;

	ptp_free_propvalue (opd->DataType, &opd->DefaultValue);
	switch (opd->FormFlag) {
	case PTP_OPFF_None:
		break;
	case PTP_OPFF_Range:
		ptp_free_propvalue (opd->DataType, &opd->FORM.Range.MinValue);
		ptp_free_propvalue (opd->DataType, &opd->FORM.Range.MaxValue);
		ptp_free_propvalue (opd->DataType, &opd->FORM.Range.StepSize);
		break;
	case PTP_OPFF_Enumeration:
		if (opd->FORM.Enum.SupportedValue) {
			for (i=0;i<opd->FORM.Enum.NumberOfValues;i++)
				ptp_free_propvalue (opd->DataType, opd->FORM.Enum.SupportedValue+i);
			free (opd->FORM.Enum.SupportedValue);
		}
		break;
	case PTP_OPFF_DateTime:
		free(opd->FORM.DateTime.String);
		break;
	case PTP_OPFF_RegularExpression:
		free(opd->FORM.RegularExpression.String);
		break;
	case PTP_OPFF_FixedLengthArray:	/* nothing to free */ /* fallthrough */
	case PTP_OPFF_ByteArray:	/* nothing to free */ /* fallthrough */
	case PTP_OPFF_LongString:
		/* Ignore these presently, we cannot unpack them, so there is nothing to be freed. */
		break;
	default:
		fprintf (stderr, "Unknown OPFF type %d\n", opd->FormFlag);
		break;
	}
}

void ptp_free_eos_event(PTPCanonEOSEvent *eos_event)
{
	/* TODO: this function only exists because of the heap allocated Filename and Keyword members of
	 * of PTPObjectInfo. This function needs to be called on every ptp_get_one_eos_event() result.
	 * There are still tons of potential memory leaks in all sorts of error paths. The only sane solution
	 * to this is, to make the Filename property a statically sized string (and drop Keywords, which is
	 * unused anyway).
	 */
	switch (eos_event->type)
	{
	case PTP_EOSEvent_ObjectAdded:
	case PTP_EOSEvent_ObjectContentChanged:
	case PTP_EOSEvent_ObjectInfoChanged:
	case PTP_EOSEvent_ObjectRemoved:
	case PTP_EOSEvent_ObjectTransfer:
		ptp_free_objectinfo(&eos_event->u.object);
		break;
	default:
		break;
	}
}


/**
 * ptp_free_params:
 * params:	PTPParams*
 *
 * Frees all data within the PTPParams struct.
 *
 * Return values: Some PTP_RC_* code.
 **/
void
ptp_free_params (PTPParams *params)
{
	free (params->cameraname);
	free (params->wifi_profiles);
	free_array (&params->storageids);
	free_array (&params->events);

	free_array_recusive (&params->objects, ptp_free_object);
	free_array_recusive (&params->canon_props, ptp_free_devicepropdesc);
	free_array_recusive (&params->eos_events, ptp_free_eos_event);
	free_array_recusive (&params->dpd_cache, ptp_free_devicepropdesc);

	ptp_free_deviceinfo (&params->deviceinfo);
}

/**
 * ptp_getststorageids:
 * params:	PTPParams*
 *
 * Gets array of (valid) StorageIDs and fills the storageids structure.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getstorageids (PTPParams* params, PTPStorageIDs* storageids)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	array_init(storageids);

	PTP_CNT_INIT(ptp, PTP_OC_GetStorageIDs);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	PTPStorageIDs all_ids;
	ptp_unpack_ArrayU32(params, data, size, &all_ids);
	free(data);

	for_each (uint32_t*, psid, all_ids)
		if ((*psid & 0xffff) != 0) // filter out invalid storageIDs once instead of everywhere
			array_push_back(storageids, *psid);

	free_array (&all_ids);
	return PTP_RC_OK;
}

/**
 * ptp_getststorageinfo:
 * params:	PTPParams*
 *		storageid		- StorageID
 *
 * Gets StorageInfo dataset of desired storage and fills storageinfo
 * structure.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getstorageinfo (PTPParams* params, uint32_t storageid,
			PTPStorageInfo* storageinfo)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_GetStorageInfo, storageid);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	memset(storageinfo, 0, sizeof(*storageinfo));
	ret = ptp_unpack_SI(params, data, storageinfo, size) ? PTP_RC_OK : PTP_ERROR_IO;
	free(data);
	return ret;
}

/**
 * ptp_getobjecthandles:
 * params:	PTPParams*
 *		storage			- StorageID
 *		objectformatcode	- ObjectFormatCode (optional)
 *		associationOH		- ObjectHandle of Association for
 *					  which a list of children is desired
 *					  (optional)
 *		objecthandles		- pointer to structute
 *
 * Fills objecthandles with structure returned by device.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobjecthandles (PTPParams* params, uint32_t storage,
			uint32_t objectformatcode, uint32_t associationOH,
			PTPObjectHandles* objecthandles)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	unsigned int	size;

	objecthandles->val = NULL;
	objecthandles->len = 0;

	PTP_CNT_INIT(ptp, PTP_OC_GetObjectHandles, storage, objectformatcode, associationOH);
	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
	if (ret == PTP_RC_OK) {
		ptp_unpack_ArrayU32(params, data, size, objecthandles);
	} else {
		if (	(storage == 0xffffffff) &&
			(objectformatcode == 0) &&
			(associationOH == 0)
		) {
			/* When we query all object handles on all stores and
			 * get an error -> just handle it as "0 handles".
			 */
			ret = PTP_RC_OK;
		}
	}
	free(data);
	return ret;
}

uint16_t
ptp_getfilesystemmanifest (PTPParams* params, uint32_t storage,
	uint32_t objectformatcode, uint32_t associationOH,
	uint64_t *numoifs, PTPObjectFilesystemInfo **oifs
) {
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned int	size = 0;
	unsigned char	*data = NULL;

	*oifs = NULL;
	*numoifs = 0;
	PTP_CNT_INIT(ptp, PTP_OC_GetFilesystemManifest, storage, objectformatcode, associationOH);
	CHECK_PTP_RC (ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_ptp11_manifest (params, data, size, numoifs, oifs);
	free(data);
	return ret;
}

/**
 * ptp_getnumobjects:
 * params:	PTPParams*
 *		storage			- StorageID
 *		objectformatcode	- ObjectFormatCode (optional)
 *		associationOH		- ObjectHandle of Association for
 *					  which a list of children is desired
 *					  (optional)
 *		numobs			- pointer to uint32_t that takes number of objects
 *
 * Fills numobs with number of objects on device.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getnumobjects (PTPParams* params, uint32_t storage,
			uint32_t objectformatcode, uint32_t associationOH,
			uint32_t* numobs)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetNumObjects, storage, objectformatcode, associationOH);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if (ptp.Nparam >= 1)
		*numobs = ptp.Param1;
	else
		return PTP_RC_GeneralError;
	return PTP_RC_OK;
}

/**
 * ptp_eos_bulbstart:
 * params:	PTPParams*
 *
 * Starts EOS Bulb capture.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_canon_eos_bulbstart (PTPParams* params)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_BulbStart);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if ((ptp.Nparam >= 1) && ((ptp.Param1 & 0x7000) == 0x2000))
		return ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_eos_capture:
 * params:	PTPParams*
 *		uint32_t*	result
 *
 * This starts a EOS400D style capture. You have to use the
 * get_eos_events to find out what resulted.
 * The return value is "0" for all OK, and "1" for capture failed. (not fully confirmed)
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_canon_eos_capture (PTPParams* params, uint32_t *result)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_RemoteRelease);
	*result = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if (ptp.Nparam >= 1)
		*result = ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_canon_eos_bulbend:
 * params:	PTPParams*
 *
 * Starts EOS Bulb capture.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_canon_eos_bulbend (PTPParams* params)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_BulbEnd);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if ((ptp.Nparam >= 1) && ((ptp.Param1 & 0x7000) == 0x2000))
		return ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_getobjectinfo:
 * params:	PTPParams*
 *		handle			- Object handle
 *		objectinfo		- pointer to objectinfo that is returned
 *
 * Get objectinfo structure for handle from device.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobjectinfo (PTPParams* params, uint32_t handle,
			PTPObjectInfo* objectinfo)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_GetObjectInfo, handle);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_OI(params, data, objectinfo, size);
	objectinfo->Handle = handle;
	free(data);
	return PTP_RC_OK;
}

/**
 * ptp_getobject:
 * params:	PTPParams*
 *		handle			- Object handle
 *		object			- pointer to data area
 *
 * Get object 'handle' from device and store the data in newly
 * allocated 'object'.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobject (PTPParams* params, uint32_t handle, unsigned char** object)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetObject, handle);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, NULL);
}

/**
 * ptp_getobject_with_size:
 * params:	PTPParams*
 *		handle			- Object handle
 *		object			- pointer to data area
 *		size			- pointer to uint, returns size of object
 *
 * Get object 'handle' from device and store the data in newly
 * allocated 'object'.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobject_with_size (PTPParams* params, uint32_t handle, unsigned char** object, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetObject, handle);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, size);
}

/**
 * ptp_getobject_to_handler:
 * params:	PTPParams*
 *		handle			- Object handle
 *		PTPDataHandler*		- pointer datahandler
 *
 * Get object 'handle' from device and store the data in newly
 * allocated 'object'.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobject_to_handler (PTPParams* params, uint32_t handle, PTPDataHandler *handler)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetObject, handle);
	return ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, handler);
}

/**
 * ptp_getobject_tofd:
 * params:	PTPParams*
 *		handle			- Object handle
 *		fd			- File descriptor to write() to
 *
 * Get object 'handle' from device and write the data to the
 * given file descriptor.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getobject_tofd (PTPParams* params, uint32_t handle, int fd)
{
	PTPContainer	ptp;
	PTPDataHandler	handler;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_GetObject, handle);
	ptp_init_fd_handler (&handler, fd);
	ret = ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, &handler);
	ptp_exit_fd_handler (&handler);
	return ret;
}

/**
 * ptp_getpartialobject:
 * params:	PTPParams*
 *		handle			- Object handle
 *		offset			- Offset into object
 *		maxbytes		- Maximum of bytes to read
 *		object			- pointer to data area
 *		len			- pointer to returned length
 *
 * Get object 'handle' from device and store the data in newly
 * allocated 'object'. Start from offset and read at most maxbytes.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getpartialobject (PTPParams* params, uint32_t handle, uint32_t offset,
			uint32_t maxbytes, unsigned char** object,
			uint32_t *len)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetPartialObject, handle, offset, maxbytes);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, len);
}

/**
 * ptp_getpartialobject_to_handler:
 * params:	PTPParams*
 *		handle			- Object handle
 *		offset			- Offset into object
 *		maxbytes		- Maximum of bytes to read
 *		handler			- a ptp data handler
 *
 * Get object 'handle' from device and send the data to the
 * data handler. Start from offset and read at most maxbytes.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getpartialobject_to_handler (PTPParams* params, uint32_t handle, uint32_t offset,
			uint32_t maxbytes, PTPDataHandler *handler)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetPartialObject, handle, offset, maxbytes);
	return ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, handler);
}

/**
 * ptp_nikon_getpartialobjectex:
 * params:	PTPParams*
 *		handle			- Object handle
 *		offset			- 64bit offset into object
 *		maxbytes		- 64bit maximum of bytes to read
 *		handler			- a ptp pata handler
 *
 * Get object 'handle' from device and send the data to the
 * data handler. Start from offset and read at most maxbytes.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_nikon_getpartialobjectex (PTPParams* params, uint32_t handle, uint64_t offset,
				uint64_t maxbytes, unsigned char** object,
				uint32_t *len)
{
	PTPContainer ptp;

	/* casts due to varargs otherwise pushing 64bit values on the stack */
	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetPartialObjectEx, handle, ((uint32_t)offset & 0xFFFFFFFF), (uint32_t)(offset >> 32), ((uint32_t)maxbytes & 0xFFFFFFFF), (uint32_t)(maxbytes >> 32));
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, len);
}


/**
 * ptp_getthumb:
 * params:	PTPParams*
 *		handle			- Object handle
 *		object			- pointer to data area
 *
 * Get thumb for object 'handle' from device and store the data in newly
 * allocated 'object'.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_getthumb (PTPParams* params, uint32_t handle, unsigned char** object, unsigned int *len)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_GetThumb, handle);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, len);
}

/**
 * ptp_nikon_getlargethumb:
 * params:	PTPParams*
 *		handle			- Object handle
 *		object			- pointer to data area
 *
 * Get a large thumb for object 'handle' from device and store the data in newly
 * allocated 'object'. This function is Nikon specific.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_nikon_getlargethumb (PTPParams* params, uint32_t handle, unsigned char** object, unsigned int *len)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetLargeThumb, handle);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, len);
}

/**
 * ptp_nikon_getobjectsize:
 * params:	PTPParams*
 *		handle			- Object handle
 *		objectsize		- size of object
 *
 * Get the 64bit objectsize for object 'handle' from device and store the size in objectsize
 * allocated 'object'. This function is Nikon specific.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_nikon_getobjectsize (PTPParams* params, uint32_t handle, uint64_t *objectsize)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size = 0;

	*objectsize = 0;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetObjectSize, handle);
	CHECK_PTP_RC (ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 8) {
		free (data);
		return PTP_RC_GeneralError;
	}

	*objectsize = dtoh64a(data);
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_deleteobject:
 * params:	PTPParams*
 *		handle			- object handle
 *		ofc			- object format code (optional)
 *
 * Deletes desired objects.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_deleteobject (PTPParams* params, uint32_t handle, uint32_t ofc)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_DeleteObject, handle, ofc);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	/* If the object is cached and could be removed, cleanse cache. */
	ptp_remove_object_from_cache(params, handle);
	return PTP_RC_OK;
}

/**
 * ptp_moveobject:
 * params:	PTPParams*
 *		handle			- source ObjectHandle
 *		storage			- destination StorageID
 *		parent			- destination parent ObjectHandle
 *
 * Move an object to a new location under the specified parent.
 * Note that unlike most calls, 0 must be passed for the parent if the destination
 * is the Storage root.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_moveobject (PTPParams* params, uint32_t handle, uint32_t storage, uint32_t parent)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_MoveObject, handle, storage, parent);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	/* If the object is cached and could be removed, cleanse cache. */
	ptp_remove_object_from_cache(params, handle);
	return PTP_RC_OK;
}

/**
 * ptp_copyobject:
 * params:	PTPParams*
 *		handle			- source ObjectHandle
 *		storage			- destination StorageID
 *		parent			- destination parent ObjectHandle
 *
 * Copy an object to a new location under the specified parent.
 * Note that unlike most calls, 0 must be passed for the parent if the destination
 * is the Storage root.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_copyobject (PTPParams* params, uint32_t handle, uint32_t storage, uint32_t parent)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CopyObject, handle, storage, parent);
	return ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
}

/**
 * ptp_sendobjectinfo:
 * params:	PTPParams*
 *		uint32_t* store		- destination StorageID on Responder
 *		uint32_t* parenthandle 	- Parent ObjectHandle on responder
 * 		uint32_t* handle	- see Return values
 *		PTPObjectInfo* objectinfo- ObjectInfo that is to be sent
 *
 * Sends ObjectInfo of file that is to be sent via SendFileObject.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : uint32_t* store	- Responder StorageID in which
 *					  object will be stored
 *		  uint32_t* parenthandle- Responder Parent ObjectHandle
 *					  in which the object will be stored
 *		  uint32_t* handle	- Responder's reserved ObjectHandle
 *					  for the incoming object
 **/
uint16_t
ptp_sendobjectinfo (PTPParams* params, uint32_t* store,
			uint32_t* parenthandle, uint32_t* handle,
			PTPObjectInfo* objectinfo)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SendObjectInfo, *store, *parenthandle);
	size = ptp_pack_OI(params, objectinfo, &data);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	*store=ptp.Param1;
	*parenthandle=ptp.Param2;
	*handle=ptp.Param3;
	return ret;
}

/**
 * ptp_sendobject:
 * params:	PTPParams*
 *		char*	object		- contains the object that is to be sent
 *		uint64_t size		- object size
 *
 * Sends object to Responder.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_sendobject (PTPParams* params, unsigned char* object, uint64_t size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SendObject);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &object, NULL);
}

/**
 * ptp_sendobject_from_handler:
 * params:	PTPParams*
 *		PTPDataHandler*		- File descriptor to read() object from
 *		uint64_t size		- File/object size
 *
 * Sends object from file descriptor by consecutive reads from this
 * descriptor.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_sendobject_from_handler (PTPParams* params, PTPDataHandler *handler, uint64_t size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_SendObject);
	return ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, handler);
}


/**
 * ptp_sendobject_fromfd:
 * params:	PTPParams*
 *		fd			- File descriptor to read() object from
 *		uint64_t size		- File/object size
 *
 * Sends object from file descriptor by consecutive reads from this
 * descriptor.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_sendobject_fromfd (PTPParams* params, int fd, uint64_t size)
{
	PTPContainer	ptp;
	PTPDataHandler	handler;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_SendObject);
	ptp_init_fd_handler (&handler, fd);
	ret = ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, &handler);
	ptp_exit_fd_handler (&handler);
	return ret;
}

#define PROPCACHE_TIMEOUT 5	/* seconds */

uint16_t
ptp_getdevicepropdesc (PTPParams* params, uint32_t propcode,
			PTPDevicePropDesc* devicepropertydesc)
{
	PTPContainer	ptp;
	uint16_t	ret = PTP_RC_OK;
	unsigned char	*data = NULL;
	unsigned int	size, offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_GetDevicePropDesc, propcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (!data) {
		ptp_debug (params, "no data received for getdevicepropdesc");
		return PTP_RC_InvalidDevicePropFormat;
	}

	if (params->device_flags & DEVICE_FLAG_OLYMPUS_XML_WRAPPED) {
#ifdef HAVE_LIBXML2
		xmlNodePtr	code;

		ret = ptp_olympus_parse_output_xml (params,(char*)data,size,&code);
		if (ret == PTP_RC_OK) {
			uint32_t x;

			if (	(xmlChildElementCount(code) == 1) &&
					(!strcmp((char*)code->name,"c1014"))
					) {
				code = xmlFirstElementChild (code);

				if (	(sscanf((char*)code->name,"p%x", &x)) &&
						(x == propcode)
						) {
					ret = parse_9301_propdesc (params, xmlFirstElementChild (code), devicepropertydesc);
					xmlFreeDoc(code->doc);
				}
			}
		} else {
			ptp_debug(params,"failed to parse output xml, ret %x?", ret);
		}
#endif
	} else {
		if (!ptp_unpack_DPD(params, data, devicepropertydesc, size, &offset)) {
			ptp_debug(params,"failed to unpack DPD of propcode 0x%04x, likely corrupted?", propcode);
			free (data);
			return PTP_RC_InvalidDevicePropFormat;
		}
	}
	free(data);
	return ret;
}


uint16_t
ptp_getdevicepropvalue (PTPParams* params, uint32_t propcode,
			PTPPropValue* value, uint16_t datatype)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size, offset = 0;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_GetDevicePropValue, propcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_DPV(params, data, &offset, size, value, datatype) ? PTP_RC_OK : PTP_ERROR_IO;
	if (ret != PTP_RC_OK)
		ptp_debug (params, "ptp_getdevicepropvalue: unpacking DPV failed");
	free(data);
	return ret;
}

uint16_t
ptp_setdevicepropvalue (PTPParams* params, uint32_t propcode,
			PTPPropValue *value, uint16_t datatype)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SetDevicePropValue, propcode);
	size=ptp_pack_DPV(params, value, &data, datatype);
	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

/**
 * ptp_ek_sendfileobjectinfo:
 * params:	PTPParams*
 *		uint32_t* store		- destination StorageID on Responder
 *		uint32_t* parenthandle 	- Parent ObjectHandle on responder
 * 		uint32_t* handle	- see Return values
 *		PTPObjectInfo* objectinfo- ObjectInfo that is to be sent
 *
 * Sends ObjectInfo of file that is to be sent via SendFileObject.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : uint32_t* store	- Responder StorageID in which
 *					  object will be stored
 *		  uint32_t* parenthandle- Responder Parent ObjectHandle
 *					  in which the object will be stored
 *		  uint32_t* handle	- Responder's reserved ObjectHandle
 *					  for the incoming object
 **/
uint16_t
ptp_ek_sendfileobjectinfo (PTPParams* params, uint32_t* store,
			uint32_t* parenthandle, uint32_t* handle,
			PTPObjectInfo* objectinfo)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_EK_SendFileObjectInfo, *store, *parenthandle);
	size=ptp_pack_OI(params, objectinfo, &data);
	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	*store=ptp.Param1;
	*parenthandle=ptp.Param2;
	*handle=ptp.Param3;
	return ret;
}

/**
 * ptp_ek_getserial:
 * params:	PTPParams*
 *		char**	serial		- contains the serial number of the camera
 *		uint32_t* size		- contains the string length
 *
 * Gets the serial number from the device. (ptp serial)
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_ek_getserial (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_EK_GetSerial);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_ek_setserial:
 * params:	PTPParams*
 *		char*	serial		- contains the new serial number
 *		uint32_t size		- string length
 *
 * Sets the serial number of the device. (ptp serial)
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_ek_setserial (PTPParams* params, unsigned char *data, unsigned int size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_EK_SetSerial);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
}

/* unclear what it does yet */
uint16_t
ptp_ek_9007 (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, 0x9007);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/* unclear what it does yet */
uint16_t
ptp_ek_9009 (PTPParams* params, uint32_t *p1, uint32_t *p2)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, 0x9009);
	*p1 = *p2 = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*p1 = ptp.Param1;
	*p2 = ptp.Param2;
	return PTP_RC_OK;
}

/* unclear yet, but I guess it returns the info from 9008 */
uint16_t
ptp_ek_900c (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, 0x900c);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
	/* returned data is 16bit,16bit,32bit,32bit */
}

/**
 * ptp_ek_settext:
 * params:	PTPParams*
 *		PTPEKTextParams*	- contains the texts to display.
 *
 * Displays the specified texts on the TFT of the camera.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_ek_settext (PTPParams* params, PTPEKTextParams *text)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = 0;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_EK_SetText);
	if (0 == (size = ptp_pack_EK_text(params, text, &data)))
		return PTP_ERROR_BADPARAM;
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

/**
 * ptp_ek_sendfileobject:
 * params:	PTPParams*
 *		char*	object		- contains the object that is to be sent
 *		uint32_t size		- object size
 *
 * Sends object to Responder.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_ek_sendfileobject (PTPParams* params, unsigned char* object, uint32_t size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_EK_SendFileObject);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &object, NULL);
}

/**
 * ptp_ek_sendfileobject_from_handler:
 * params:	PTPParams*
 *		PTPDataHandler*	handler	- contains the handler of the object that is to be sent
 *		uint32_t size		- object size
 *
 * Sends object to Responder.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_ek_sendfileobject_from_handler (PTPParams* params, PTPDataHandler*handler, uint32_t size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_EK_SendFileObject);
	return ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, handler);
}

/*************************************************************************
 *
 * Canon PTP extensions support
 *
 * (C) Nikolai Kopanygin 2003
 *
 *************************************************************************/

#if 0
/* Disabled / unused code */

/**
 * ptp_canon_getpartialobjectinfo:
 * params:	PTPParams*
 *		uint32_t handle		- ObjectHandle
 *		uint32_t p2 		- Not fully understood parameter
 *					  0 - returns full size
 *					  1 - returns thumbnail size (or EXIF?)
 *
 * Gets form the responder the size of the specified object.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : uint32_t* size	- The object size
 *		  uint32_t* rp2		- Still unknown return parameter
 *					  (perhaps upper 32bit of size)
 *
 *
 **/
uint16_t
ptp_canon_getpartialobjectinfo (PTPParams* params, uint32_t handle, uint32_t p2,
			uint32_t* size, uint32_t* rp2)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetPartialObjectInfo, handle, p2);
	*size = *rp2 = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*size=ptp.Param1;
	*rp2=ptp.Param2;
	return PTP_RC_OK;
}

/**
 * ptp_canon_get_mac_address:
 * params:	PTPParams*
 *					  value 0 works.
 * Gets the MAC address of the wireless transmitter.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : unsigned char* mac	- The MAC address
 *
 **/
uint16_t
ptp_canon_get_mac_address (PTPParams* params, unsigned char **mac)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetMACAddress);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, mac, NULL);
}

/**
 * ptp_canon_get_directory:
 * params:	PTPParams*

 * Gets the full directory of the camera.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : PTPObjectHandles        *handles	- filled out with handles
 * 		  PTPObjectInfo           **oinfos	- allocated array of PTP Object Infos
 * 		  uint32_t                **flags	- allocated array of CANON Flags
 *
 **/
uint16_t
ptp_canon_get_directory (PTPParams* params,
	PTPObjectHandles	*handles,
	PTPObjectInfo		**oinfos,	/* size(handles->len) */
	uint32_t		**flags		/* size(handles->len) */
) {
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetDirectory);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, NULL));
	ret = ptp_unpack_canon_directory(params, data, ptp.Param1, handles, oinfos, flags);
	free (data);
	return ret;
}

/**
 * ptp_canon_gettreeinfo:
 * params:	PTPParams*
 *              uint32_t *out
 *
 * Switches the camera display to on and lets the user
 * select what to transfer. Sends a 0xc011 event when started
 * and 0xc013 if direct transfer aborted.
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_gettreeinfo (PTPParams* params, uint32_t *out)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetTreeInfo, 0xf);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	if (ptp.Nparam > 0)
		*out = ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_canon_getpairinginfo:
 * params:	PTPParams*
 *              int nr
 *
 * Get the pairing information.
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_getpairinginfo (PTPParams* params, uint32_t nr, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetPairingInfo, nr);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_canon_get_target_handles:
 * params:	PTPParams*
 *              PTPCanon_directtransfer_entry **out
 *              unsigned int *outsize
 *
 * Retrieves direct transfer entries specifying the images to transfer
 * from the camera (to be retrieved after 0xc011 event).
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_gettreesize (PTPParams* params,
	PTPCanon_directtransfer_entry **entries, unsigned int *cnt)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size = 0, i, offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetTreeSize);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	*cnt = dtoh32o(data, offset);
	*entries = calloc((*cnt), sizeof(PTPCanon_directtransfer_entry));
	if (!*entries)
		goto error;
	for (i=0;i<*cnt;i++) {
		(*entries)[i].handle = dtoh32o(data, offset);
		if (!ptp_unpack_string(params, data, &offset, size, &(*entries)[i].str))
			break;
	}
	free (data);
	return PTP_RC_OK;
error:
	free (data);
	return PTP_RC_GeneralError;
}
#endif

/**
 * ptp_canon_checkevent:
 * params:	PTPParams*
 *
 * The camera has a FIFO stack, in which it accumulates events.
 * Partially these events are communicated also via the USB interrupt pipe
 * according to the PTP USB specification, partially not.
 * This operation returns from the device a block of data, empty,
 * if the event stack is empty, or filled with an event's data otherwise.
 * The event is removed from the stack in the latter case.
 * The Remote Capture app sends this command to the camera all the time
 * of connection, filling with it the gaps between other operations.
 *
 * Return values: Some PTP_RC_* code.
 * Upon success : PTPUSBEventContainer* event	- is filled with the event data
 *						  if any
 *                int *isevent			- returns 1 in case of event
 *						  or 0 otherwise
 **/
uint16_t
ptp_canon_checkevent (PTPParams* params, PTPContainer* event, int* isevent)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_CheckEvent);
	*isevent=0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (data && size) { /* check if we had a successful call with data */
		ptp_unpack_canon_event(params, data, event, size);
		*isevent=1;
		free(data);
	}
	return PTP_RC_OK;
}

uint16_t
ptp_add_event (PTPParams *params, PTPContainer *event)
{
	array_push_back (&params->events, *event);
	return PTP_RC_OK;
}

/* CANON EOS fast directory mode: uses ptp_canon_eos_getobjectinfoex to get list of
 * ObjectInfos instead of just a list of handles that have to be queried then one by one.*/
static uint16_t
ptp_list_folder_eos (PTPParams *params, uint32_t storage, uint32_t handle, PTPObjectHandles *children) {
	unsigned int	last = 0, changed = 0;
	PTPCANONFolderEntry *tmp = NULL;
	unsigned int	nroftmp = 0;

	ptp_debug (params, "list_folder_eos(storage=0x%08x, handle=0x%08x)", storage, handle);
	/* the following call currently always fails for the R5m2 for an unkown reason */
	CHECK_PTP_RC (ptp_canon_eos_getobjectinfoex (
		params, storage, handle ? handle : PTP_HANDLER_SPECIAL, 0x100000, &tmp, &nroftmp));

	if (children)
		array_extend_capacity(children, nroftmp);

	/* convert read entries into objectinfos */
	for (unsigned int i=0; i<nroftmp; i++) {
		PTPObject	*ob = NULL;
		unsigned int	j;

		if (children)
			children->val[children->len++] = tmp[i].ObjectHandle;

		/* TODO: the following is actually slow when adding new entries, as the complete list needs to be traversed.
			* Make better use of the fact that the list is sorted. */
		for (j=0; j<params->objects.len; j++) {
			if (params->objects.val[(last+j) % params->objects.len].oid == tmp[i].ObjectHandle)  {
				ob = &params->objects.val[(last+j) % params->objects.len];
				break;
			}
		}
		if (ob == NULL) {
			ptp_debug (params, "adding new object: handle 0x%08x (nrofobs=%d,j=%d)", tmp[i].ObjectHandle, params->objects.len, j);

			array_push_back_empty (&params->objects, &ob);

			ob->oid = tmp[i].ObjectHandle;
			ob->oi.Handle = tmp[i].ObjectHandle;
			ob->flags = 0;

			ob->oi.StorageID = storage;
			ob->flags |= PTPOBJECT_STORAGEID_LOADED;
			ob->oi.ParentObject = handle == PTP_HANDLER_SPECIAL ? 0 : handle;
			ob->flags |= PTPOBJECT_PARENTOBJECT_LOADED;
			ob->oi.Filename = strdup(tmp[i].Filename);
			ob->oi.ObjectFormat = tmp[i].ObjectFormatCode;

			ptp_debug (params, "   flags %x", tmp[i].Flags);
			if (tmp[i].Flags & 0x1)
				ob->oi.ProtectionStatus = PTP_PS_ReadOnly;
			else
				ob->oi.ProtectionStatus = PTP_PS_NoProtection;
			ob->canon_flags = tmp[i].Flags;
			ob->oi.ObjectSize = tmp[i].ObjectSize;
			ob->oi.CaptureDate = tmp[i].Time;
			ob->oi.ModificationDate = tmp[i].Time;
			ob->flags |= PTPOBJECT_OBJECTINFO_LOADED;

			/*log_objectinfo(params, &ob->oi);*/
			last = params->objects.len;
			changed = 1;
		} else {
			ptp_debug (params, "adding old object: handle 0x%08x (nrofobs=%d,j=%d)", tmp[i].ObjectHandle, params->objects.len, j);
			/* for speeding up search */
			last = (last+j) % params->objects.len;
			if (handle != PTP_HANDLER_SPECIAL) {
				ob->oi.ParentObject = handle;
				ob->flags |= PTPOBJECT_PARENTOBJECT_LOADED;
			}
			if (storage != PTP_HANDLER_SPECIAL) {
				ob->oi.StorageID = storage;
				ob->flags |= PTPOBJECT_STORAGEID_LOADED;
			}
		}
	}
	free (tmp);

	if (changed)
		ptp_objects_sort (params);

	return PTP_RC_OK;
}

uint16_t
ptp_list_folder (PTPParams *params, uint32_t storage, uint32_t handle, PTPObjectHandles *children) {
	unsigned int		changed, last;
	uint16_t		ret;
	uint32_t		xhandle = handle;
	PTPObjectHandles	handles = {0};

	ptp_debug (params, "ptp_list_folder(storage=0x%08x, handle=0x%08x)", storage, handle);

	if (children)
		array_init(children);

	/* TODO: remove special handling of root folder by introducing a 'root' object */
	if (handle == PTP_HANDLER_SPECIAL)
		handle = 0;

	/* camera_init calls this once for each storage with handle == 0 and children == NULL to read the root folder,
	 * subsequent calls asking for the root folder will simply return the cached list. This only works since we
	 * assume the entries in the root folder can never change. */
	if (!handle && children && params->objects.len != 0) {
		for_each (PTPObject*, pob, params->objects)
			if (pob->oi.ParentObject == 0 && pob->oi.StorageID == storage)
				array_push_back(children, pob->oid);
		return PTP_RC_OK;
	}

	if (handle) { /* 0 is the virtual root */
		PTPObject		*ob;
		/* first check if object itself is loaded, and get its objectinfo. */
		ret = ptp_object_want (params, handle, PTPOBJECT_OBJECTINFO_LOADED, &ob);
		if (ret != PTP_RC_OK)
			return ret;
		if (ob->oi.ObjectFormat != PTP_OFC_Association)
			return PTP_RC_GeneralError;
		if (ob->flags & PTPOBJECT_DIRECTORY_LOADED) {
			if (children) {
				for_each (PTPObject*, pob, params->objects)
					if (pob->oi.ParentObject == handle)
						array_push_back(children, pob->oid);
			}
			return PTP_RC_OK;
		}
		ob->flags |= PTPOBJECT_DIRECTORY_LOADED;
		/*log_objectinfo(params, handle, &ob->oi);*/
	}

	/* Canon EOS Fast directory strategy */
	if ((params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
	    storage != PTP_HANDLER_SPECIAL &&
	    ptp_operation_issupported(params, PTP_OC_CANON_EOS_GetObjectInfoEx)) {
		ret = ptp_list_folder_eos (params, storage, handle, children);
		if (ret == PTP_RC_OK)
			return ret;
	}

#if 0 /* apple devices report it, but the conrtent they have does not match the standard somehow. Needs further debugging */
	if (ptp_operation_issupported(params, PTP_OC_GetFilesystemManifest)) {
		uint64_t		numoifs = 0;
		PTPObjectFilesystemInfo	*oifs = NULL;

		if (storage == PTP_HANDLER_SPECIAL) storage = 0;
		ret = ptp_getfilesystemmanifest (params, storage, 0, handle, &numoifs, &oifs);
		if (ret != PTP_RC_OK || !numoifs)
			goto fallback;

		last = changed = 0;
		for (i=0;i<numoifs;i++) {
			PTPObject	*ob;
			unsigned int	j;

			ob = NULL;
			for (j=0;j<params->objects.len;j++) {
				if (params->objects[(last+j)%params->objects.len].oid == oifs[i].ObjectHandle)  {
					ob = &params->objects[(last+j)%params->objects.len];
					break;
				}
			}
			if (j == params->objects.len) {
				ptp_debug (params, "adding new object: handle 0x%08x (nrofobs=%d,j=%d)", oifs[i].ObjectHandle, params->objects.len,j);
				array_push_back_empty (&params->objects, &ob);
				ob->oid = oifs[i].ObjectHandle;
				ob->oi.Handle = oifs[i].ObjectHandle;
				changed = 1;
			} else {
				ptp_debug (params, "adding old object: handle 0x%08x (nrofobs=%d,j=%d)", oifs[i].ObjectHandle, params->objects.len,j);
				ob = &params->objects[(last+j)%params->objects.len];
				/* for speeding up search */
				last = (last+j)%params->objects.len;
			}

			ob->oi.StorageID 		= oifs[i].StorageID;
			ob->oi.ObjectFormat 		= oifs[i].ObjectFormat;
			ob->oi.ProtectionStatus 	= oifs[i].ProtectionStatus;
			ob->oi.ObjectSize		= oifs[i].ObjectSize64;
			ob->oi.ParentObject		= oifs[i].ParentObject;

			/* bad iOS, returns StorageID instead of 0x0 */
			if (ob->oi.ParentObject == oifs[i].StorageID) {
				ptp_debug (params, "objectid 0x%08x aka %s has parent %08x, rewriting to 0", oifs[i].ObjectHandle, oifs[i].Filename, oifs[i].ParentObject);
				ob->oi.ParentObject = 0;
			}

			ob->oi.AssociationType		= oifs[i].AssociationType;
			ob->oi.AssociationDesc		= oifs[i].AssociationDesc;
			ob->oi.SequenceNumber		= oifs[i].SequenceNumber;
			ob->oi.Filename			= oifs[i].Filename; /* hand over memory ownership */
			ob->oi.ModificationDate		= oifs[i].ModificationDate;
			/* FIXME: most of it ... but not the image sizes */
			ob->flags			|= PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_STORAGEID_LOADED|PTPOBJECT_PARENTOBJECT_LOADED;
		}
		free (oifs);
		if (changed) ptp_objects_sort (params);
		return PTP_RC_OK;
	}
fallback:
#endif

	if (handle == 0)
		xhandle = PTP_HANDLER_SPECIAL; /* 0 would mean all */
	ret = ptp_getobjecthandles (params, storage, 0, xhandle, &handles);
	if (ret == PTP_RC_ParameterNotSupported) {/* try without storage */
		storage = PTP_HANDLER_SPECIAL;
		ret = ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0, xhandle, &handles);
	}
	if (ret == PTP_RC_ParameterNotSupported) { /* fall back to always supported method */
		xhandle = PTP_HANDLER_SPECIAL;
		handle = PTP_HANDLER_SPECIAL;
		ret = ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0, 0, &handles);
	}
	if (ret != PTP_RC_OK)
		return ret;
	last = changed = 0;
	for_each (uint32_t*, phandle, handles) {
		PTPObject	*ob = NULL;
		unsigned int	j;

		/* TODO: the following actually slow when adding new entries, as the complete list needs to be traversed.
		 * Make better use of the fact that the list is sorted. */
		for (j=0;j<params->objects.len;j++) {
			if (params->objects.val[(last+j) % params->objects.len].oid == *phandle)  {
				ob = &params->objects.val[(last+j) % params->objects.len];
				break;
			}
		}
		if (ob == NULL) {
			ptp_debug (params, "adding new object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len, j);

			array_push_back_empty (&params->objects, &ob);

			ob->oid = *phandle;
			ob->oi.Handle = *phandle;
			ob->flags = 0;
			/* root directory list files might return all files, so avoid tagging it */
			if (handle != PTP_HANDLER_SPECIAL && handle) {
				ptp_debug (params, "  parent 0x%08x", handle);
				if (*phandle == handle) { /* EOS bug where handle == parent(handle) */
					ob->oi.ParentObject = 0;
				} else {
					ob->oi.ParentObject = handle;
				}
				ob->flags |= PTPOBJECT_PARENTOBJECT_LOADED;
			}
			if (storage != PTP_HANDLER_SPECIAL) {
				ptp_debug (params, "  storage 0x%08x", storage);
				ob->oi.StorageID = storage;
				ob->flags |= PTPOBJECT_STORAGEID_LOADED;
			}
			changed = 1;
		} else {
			ptp_debug (params, "adding old object: handle 0x%08x (nrofobs=%d,j=%d)", *phandle, params->objects.len, j);
			/* for speeding up search */
			last = (last+j) % params->objects.len;
			if (handle != PTP_HANDLER_SPECIAL) {
				ob->oi.ParentObject = handle;
				ob->flags |= PTPOBJECT_PARENTOBJECT_LOADED;
			}
			if (storage != PTP_HANDLER_SPECIAL) {
				ob->oi.StorageID = storage;
				ob->flags |= PTPOBJECT_STORAGEID_LOADED;
			}
		}
	}
	if (changed)
		ptp_objects_sort (params);
	if (children)
		*children = handles;
	else
		free_array (&handles);
	return PTP_RC_OK;
}


static void
handle_event_internal (PTPParams *params, PTPContainer *event)
{
	/* handle some PTP stack internal events */
	switch (event->Code) {
	case PTP_EC_DevicePropChanged: {
		/* mark the property for a forced refresh on the next query */
		PTPDevicePropDesc* dpd_in_cache = ptp_find_dpd_in_cache(params, event->Param1);
		if (dpd_in_cache)
			dpd_in_cache->timestamp = 0;
		break;
	}
	case PTP_EC_StoreAdded:
	case PTP_EC_StoreRemoved: {
		/* FIXME: if we just remove 1 out of many storages, we do not need to invalidate/reload the entire tree? */

		/* refetch storage IDs and also invalidate whole object tree */
		free_array (&params->storageids);
		ptp_getstorageids (params, &params->storageids);

		/* free object storage as it might be associated with the storage ids */
		/* FIXME: enhance and just delete the ones from the storage */
		free_array_recusive (&params->objects, ptp_free_object);

		params->storagechanged		= 1;
		break;
	}
	default: /* check if we should handle it internally too */
		break;
	}
}

uint16_t
ptp_check_event_queue (PTPParams *params)
{
	PTPContainer	event;
	uint16_t	ret;

	/* We try to do a event check without I/O */
	/* Basically this means just looking at the meanwhile queued events */

	ret = params->event_check_queue(params,&event);

	if (ret == PTP_RC_OK) {
		ptp_debug (params, "event: nparams=0x%X, code=0x%X, trans_id=0x%X, p1=0x%X, p2=0x%X, p3=0x%X",
		           event.Nparam,event.Code,event.Transaction_ID, event.Param1, event.Param2, event.Param3);
		ptp_add_event (params, &event);
		handle_event_internal (params, &event);
	}
	if (ret == PTP_ERROR_TIMEOUT) /* ok, just new events */
		ret = PTP_RC_OK;
	return ret;
}

uint16_t
ptp_check_event (PTPParams *params)
{
	PTPContainer	event;
	uint16_t	ret;

	if (params->deviceinfo.VendorExtensionID == PTP_VENDOR_NIKON) {
		PTPEvents events = {0};
		if (ptp_operation_issupported(params, PTP_OC_NIKON_GetEventEx)) {
			ret = ptp_nikon_check_eventex(params, &events.val, &events.len);
		} else if (ptp_operation_issupported(params, PTP_OC_NIKON_GetEvent)) {
			/* Method offered by Nikon DSLR, Nikon 1, and some older Nikon Coolpix P*
			 * The Nikon Coolpix P2 however does not return anything. So if we never get
			 * events from here, use the ptp "interrupt" method */
			ret = ptp_nikon_check_event(params, &events.val, &events.len);
		} else {
			ret = PTP_RC_OperationNotSupported;
		}
		if (ret != PTP_RC_OperationNotSupported)
			CHECK_PTP_RC(ret);

		if (events.len) {
			for_each (PTPContainer*, pevt, events)
				handle_event_internal (params, pevt);
			array_append (&params->events, &events);
			params->event90c7works = 1;
		}
		if (params->event90c7works)
			return PTP_RC_OK;
		/* fall through to generic event handling */
	}

	/* should not get here ... EOS has no normal PTP events and another queue handling. */
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
		ptp_operation_issupported(params, PTP_OC_CANON_EOS_GetEvent)
	) {
		return PTP_RC_OK;
	}

	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
		ptp_operation_issupported(params, PTP_OC_CANON_CheckEvent)
	) {
		int isevent;

		CHECK_PTP_RC(ptp_canon_checkevent (params,&event,&isevent));

		if (isevent) {
			ret = PTP_RC_OK;
			goto store_event;
		}
		/* Event Emulate Mode 0 (unset) and 1-5 get interrupt events. 6-7 does not. */
		if (params->canon_event_mode > 5)
			return PTP_RC_OK;

		/* FIXME: fallthrough or return? */
#ifdef __APPLE__
		/* the libusb 1 on darwin currently does not like polling
		 * for interrupts, they have no timeout for it. 2010/08/23
		 * Check back in 2011 or so. -Marcus
		 */
		return PTP_RC_OK;
#endif
	}

	/* check the (USB) interrupt based event queue */
	ret = params->event_check(params,&event);

store_event:
	if (ret == PTP_RC_OK) {
		ptp_debug (params, "event: nparams=0x%X, code=0x%X, trans_id=0x%X, p1=0x%X, p2=0x%X, p3=0x%X", event.Nparam,event.Code,event.Transaction_ID, event.Param1, event.Param2, event.Param3);
		ptp_add_event (params, &event);

		handle_event_internal (params, &event);
	}
	if (ret == PTP_ERROR_TIMEOUT) /* ok, just new events */
		ret = PTP_RC_OK;
	return ret;
}

uint16_t
ptp_wait_event (PTPParams *params)
{
	PTPContainer	event;
	uint16_t	ret;

	ret = params->event_wait(params,&event);
	if (ret == PTP_RC_OK) {
		ptp_debug (params, "event: nparams=0x%X, code=0x%X, trans_id=0x%X, p1=0x%X, p2=0x%X, p3=0x%X", event.Nparam,event.Code,event.Transaction_ID, event.Param1, event.Param2, event.Param3);
		ptp_add_event (params, &event);

		handle_event_internal (params, &event);
	}
	if (ret == PTP_ERROR_TIMEOUT) /* ok, just new events */
		ret = PTP_RC_OK;
	return ret;
}

int
ptp_get_one_event(PTPParams *params, PTPContainer *event)
{
	if (params->events.len == 0)
		return 0;
	array_pop_front(&params->events, event);
	return 1;
}

/**
 * ptp_get_one_event_by_type:
 *
 * Check if one specific event has appeared in the queue, and return it back to us, and remove it from the queue.

 * params:	PTPParams*	in: params
 * 		code		in: event code
 * 		event		out: event container
 *
 * Return values: 1 if removed, 0 if not.
 */
int
ptp_get_one_event_by_type(PTPParams *params, uint16_t code, PTPContainer *event)
{
	for_each (PTPContainer*, pevt, params->events) {
		if (pevt->Code == code) {
			*event = *pevt;
			array_remove(&params->events, pevt);
			return 1;
		}
	}
	return 0;
}

PTPDevicePropDesc*
ptp_find_dpd_in_cache(PTPParams *params, uint32_t dpc)
{
	for_each (PTPDevicePropDesc*, pdpd, params->dpd_cache)
		if (pdpd->DevicePropCode == dpc)
			return pdpd;
	return NULL;
}

/**
 * ptp_canon_eos_getevent:
 *
 * This retrieves configuration status/updates/changes
 * on EOS cameras. It reads a datablock which has a list of variable
 * sized structures.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_eos_getevent (PTPParams* params, PTPCanonEOSEvents *eos_events)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int 	size;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetEvent);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_EOS_events(params, data, size, eos_events);
	free (data);
	return PTP_RC_OK;
}

uint16_t
ptp_check_eos_events (PTPParams *params)
{
	PTPCanonEOSEvents events = {0};

	while (1) { /* call it repeatedly until the camera does not report any */
		CHECK_PTP_RC(ptp_canon_eos_getevent (params, &events));
		if (!events.len)
			return PTP_RC_OK;

		array_append(&params->eos_events, &events);
	}
	return PTP_RC_OK;
}

int
ptp_get_one_eos_event (PTPParams *params, PTPCanonEOSEvent *eos_event)
{
	if (!params->eos_events.len)
		return 0;

	array_pop_front(&params->eos_events, eos_event);
	return 1;
}


uint16_t
ptp_canon_eos_getdevicepropdesc (PTPParams* params, uint16_t propcode,
	PTPDevicePropDesc *dpd)
{
	PTPDevicePropDesc *cached_dpd = ptp_find_eos_devicepropdesc(params, propcode);

	if (!cached_dpd)
		return PTP_RC_Undefined;

	duplicate_DevicePropDesc(cached_dpd, dpd);

	return PTP_RC_OK;
}


uint16_t
ptp_canon_eos_getstorageids (PTPParams* params, PTPStorageIDs* storageids)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetStorageIDs);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_ArrayU32(params, data, size, storageids);
	free(data);
	return PTP_RC_OK;
}

uint16_t
ptp_canon_eos_getstorageinfo (PTPParams* params, uint32_t p1, unsigned char **data, unsigned int *size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetStorageInfo, p1);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
	/* FIXME: do stuff with data */
}

uint16_t
ptp_canon_eos_getobjectinfoex (
	PTPParams* params, uint32_t storageid, uint32_t handle, uint32_t unk,
	PTPCANONFolderEntry **entries, unsigned int *nrofentries
) {
	PTPContainer	ptp;
	unsigned char	*data = NULL, *xdata;
	unsigned int	size, i;

	*entries = 0;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetObjectInfoEx, storageid, handle, unk);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (!data) {
		*nrofentries = 0;
		return PTP_RC_OK;
	}

	if (size < 4)
		goto error;
	/* check for integer overflow */
	if (dtoh32a(data) >= INT_MAX/sizeof(PTPCANONFolderEntry))
		goto error;

	*nrofentries = dtoh32a(data);
	*entries = calloc(*nrofentries , sizeof(PTPCANONFolderEntry));
	if (!*entries)
		goto error;

	xdata = data+sizeof(uint32_t);
	for (i=0;i<*nrofentries;i++) {
		unsigned int entrysize;

		if (4 + (xdata - data) > size) {
			ptp_debug (params, "reading canon FEs run over read data size? (1)\n");
			goto error;
		}
		entrysize = dtoh32a(xdata);
		if ((entrysize + (xdata-data)) > size) {
			ptp_debug (params, "reading canon FEs run over read data size? (2)\n");
			goto error;
		}
		if (entrysize < 4 + 48 + 4)  {
			ptp_debug (params, "%d entry size %d does not match expected 56\n", i, entrysize);
			goto error;
		}
		ptp_unpack_Canon_EOS_FE (params, &xdata[4], entrysize - 4, &((*entries)[i]));
		xdata += entrysize;
	}
	free (data);
	return PTP_RC_OK;
error:
	free (*entries);
	*entries = NULL;
	*nrofentries = 0;
	free (data);
	return PTP_RC_GeneralError;
}

/**
 * ptp_canon_eos_getpartialobject:
 *
 * This retrieves a part of an PTP object identified by the object handle.
 * The id originates from 0x9116 call.
 * After finishing it, we seem to need to call ptp_canon_eos_enddirecttransfer.
 *
 * params:	PTPParams*
 * 		handle		ObjectHandle
 * 		offset		The offset where to start the data transfer
 *		xsize		Size in bytes of the transfer to do
 *		data		Pointer that receives the malloc()ed memory of the transfer.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_canon_eos_getpartialobject (PTPParams* params, uint32_t handle, uint32_t offset, uint32_t xsize, unsigned char**data)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetPartialObject, handle, offset, xsize);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, NULL);
}

/**
 * ptp_canon_eos_getpartialobjectex:
 *
 * This retrieves a part of an PTP object identified by the object handle.
 * The id originates from 0x9116 call.
 * After finishing it, we seem to need to call ptp_canon_eos_enddirecttransfer.
 *
 * params:	PTPParams*
 * 		handle		ObjectHandle
 * 		offset		The offset where to start the data transfer
 *		xsize		Size in bytes of the transfer to do
 *		data		Pointer that receives the malloc()ed memory of the transfer.
 *
 * Return values: Some PTP_RC_* code.
 *
 */
uint16_t
ptp_canon_eos_getpartialobjectex (PTPParams* params, uint32_t handle, uint32_t offset, uint32_t xsize, unsigned char**data)
{
	PTPContainer	ptp;

/* 5bf19091  00008001  00001000  00000000  */
/* handle    offset    size      ? 64bit part ? */
	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetPartialObjectEx, handle, offset, xsize, 0);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, NULL);
}

uint16_t
ptp_canon_eos_setdevicepropvalueex (PTPParams* params, unsigned char* data, unsigned int size)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_SetDevicePropValueEx);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
}

uint16_t
ptp_canon_eos_setdevicepropvalue (PTPParams* params,
	uint16_t propcode, PTPPropValue *value, uint16_t datatype
) {
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTPDevicePropDesc *dpd = ptp_find_eos_devicepropdesc(params, propcode);
	if (!dpd)
		return PTP_RC_Undefined;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_SetDevicePropValueEx);

	switch (propcode) {
	case PTP_DPC_CANON_EOS_ImageFormat:
	case PTP_DPC_CANON_EOS_ImageFormatCF:
	case PTP_DPC_CANON_EOS_ImageFormatSD:
	case PTP_DPC_CANON_EOS_ImageFormatExtHD:
		/* special handling of ImageFormat properties */
		size = 8 + ptp_pack_EOS_ImageFormat( params, NULL, value->u16 );
		data = malloc( size );
		if (!data) return PTP_RC_GeneralError;
		ptp_pack_EOS_ImageFormat( params, data + 8, value->u16 );
		break;
	case PTP_DPC_CANON_EOS_CustomFuncEx:
		/* special handling of CustomFuncEx properties */
		ptp_debug (params, "ptp2/ptp_canon_eos_setdevicepropvalue: setting EOS prop %x to %s",propcode,value->str);
		size = 8 + ptp_pack_EOS_CustomFuncEx( params, NULL, value->str );
		data = malloc( size );
		if (!data) return PTP_RC_GeneralError;
		ptp_pack_EOS_CustomFuncEx( params, data + 8, value->str );
		break;
	default:
		if (datatype != PTP_DTC_STR)
			size = sizeof(uint32_t)*3;
		else
			size = strlen(value->str) + 1 + 8;
		data = calloc(size,sizeof(char));
		if (!data) return PTP_RC_GeneralError;
		switch (datatype) {
		case PTP_DTC_INT8:
		case PTP_DTC_UINT8:
			/*fprintf (stderr, "%x -> %d\n", propcode, value->u8);*/
			htod8a(&data[8], value->u8);
			break;
		case PTP_DTC_UINT16:
		case PTP_DTC_INT16:
			/*fprintf (stderr, "%x -> %d\n", propcode, value->u16);*/
			htod16a(&data[8], value->u16);
			break;
		case PTP_DTC_INT32:
		case PTP_DTC_UINT32:
			/*fprintf (stderr, "%x -> %d\n", propcode, value->u32);*/
			htod32a(&data[8], value->u32);
			break;
		case PTP_DTC_STR:
			strcpy((char*)data + 8, value->str);
			break;
		}
	}

	htod32a(&data[0], size);
	htod32a(&data[4], propcode);

	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free (data);
	if (ret == PTP_RC_OK) {
		/* commit to cache only after successful setting */
		switch (propcode) {
		case PTP_DPC_CANON_EOS_ImageFormat:
		case PTP_DPC_CANON_EOS_ImageFormatCF:
		case PTP_DPC_CANON_EOS_ImageFormatSD:
		case PTP_DPC_CANON_EOS_ImageFormatExtHD:
			/* special handling of ImageFormat properties */
			dpd->CurrentValue.u16 = value->u16;
			break;
		case PTP_DPC_CANON_EOS_CustomFuncEx:
			/* special handling of CustomFuncEx properties */
			free(dpd->CurrentValue.str);
			dpd->CurrentValue.str = strdup( value->str );
			break;
		default:
			switch (datatype) {
			case PTP_DTC_INT8:
			case PTP_DTC_UINT8:
				dpd->CurrentValue.u8 = value->u8;
				break;
			case PTP_DTC_UINT16:
			case PTP_DTC_INT16:
				dpd->CurrentValue.u16 = value->u16;
				break;
			case PTP_DTC_INT32:
			case PTP_DTC_UINT32:
				dpd->CurrentValue.u32 = value->u32;
				break;
			case PTP_DTC_STR:
				free (dpd->CurrentValue.str);
				dpd->CurrentValue.str = strdup(value->str);
				break;
			}
		}
	}
	return ret;
}

/**
 * ptp_canon_getpartialobject:
 *
 * This operation is used to read from the device a data
 * block of an object from a specified offset.
 *
 * params:	PTPParams*
 *      uint32_t handle - the handle of the requested object
 *      uint32_t offset - the offset in bytes from the beginning of the object
 *      uint32_t size - the requested size of data block to read
 *      uint32_t pos - 1 for the first block, 2 - for a block in the middle,
 *                  3 - for the last block
 *
 * Return values: Some PTP_RC_* code.
 *      char **block - the pointer to the block of data read
 *      uint32_t* readnum - the number of bytes read
 *
 **/
uint16_t
ptp_canon_getpartialobject (PTPParams* params, uint32_t handle,
				uint32_t offset, uint32_t size,
				uint32_t pos, unsigned char** block,
				uint32_t* readnum)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetPartialObjectEx, handle, offset, size, pos);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, NULL));
	*block=data;
	*readnum=ptp.Param1;
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_canon_getviewfinderimage:
 *
 * This operation can be used to read the image which is currently
 * in the camera's viewfinder. The image size is 320x240, format is JPEG.
 * Of course, prior to calling this operation, one must turn the viewfinder
 * on with the CANON_ViewfinderOn command.
 * Invoking this operation many times, one can get live video from the camera!
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *      char **image - the pointer to the read image
 *      unit32_t *size - the size of the image in bytes
 *
 **/
uint16_t
ptp_canon_getviewfinderimage (PTPParams* params, unsigned char** image, uint32_t* size)
{
	PTPContainer	ptp;
	unsigned int	datasize = 0;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetViewfinderImage);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, image, &datasize));
	if (ptp.Param1 > datasize) {
		ptp_debug (params, "param1 is %d, but size is only %d", ptp.Param1, datasize);
		free(*image);
		return PTP_RC_GeneralError;
	}
	*size=ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_canon_getchanges:
 *
 * This is an interesting operation, about the effect of which I am not sure.
 * This command is called every time when a device property has been changed
 * with the SetDevicePropValue operation, and after some other operations.
 * This operation reads the array of Device Properties which have been changed
 * by the previous operation.
 * Probably, this operation is even required to make those changes work.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *      uint16_t** props - the pointer to the array of changed properties
 *      uint32_t* propnum - the number of elements in the *props array
 *
 **/
uint16_t
ptp_canon_getchanges (PTPParams* params, uint16_t** props, uint32_t* propnum)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	unsigned int	size = 0, offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetChanges);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ret = ptp_unpack_uint16_t_array(params, data, &offset, size, props, propnum) ? PTP_RC_OK : PTP_ERROR_IO;
	free(data);
	return ret;
}

/**
 * ptp_canon_getobjectinfo:
 *
 * This command reads a specified object's record in a device's filesystem,
 * or the records of all objects belonging to a specified folder (association).
 *
 * params:	PTPParams*
 *      uint32_t store - StorageID,
 *      uint32_t p2 - Yet unknown (0 value works OK)
 *      uint32_t parent - Parent Object Handle
 *                      # If Parent Object Handle is 0xffffffff,
 *                      # the Parent Object is the top level folder.
 *      uint32_t handle - Object Handle
 *                      # If Object Handle is 0, the records of all objects
 *                      # belonging to the Parent Object are read.
 *                      # If Object Handle is not 0, only the record of this
 *                      # Object is read.
 *
 * Return values: Some PTP_RC_* code.
 *      PTPCANONFolderEntry** entries - the pointer to the folder entry array
 *      uint32_t* entnum - the number of elements of the array
 *
 **/
uint16_t
ptp_canon_getobjectinfo (PTPParams* params, uint32_t store, uint32_t p2,
			    uint32_t parent, uint32_t handle,
			    PTPCANONFolderEntry** entries, uint32_t* entnum)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	i, size;

	*entnum = 0;
	*entries = NULL;
	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetObjectInfoEx, store, p2, parent, handle);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (ptp.Param1 > size/PTP_CANON_FolderEntryLen) {
		ptp_debug (params, "param1 is %d, size is only %d", ptp.Param1, size);
		goto error;
	}

	*entnum = ptp.Param1;
	*entries= calloc(*entnum, sizeof(PTPCANONFolderEntry));
	if (*entries == NULL)
		goto error;
	for(i=0; i<(*entnum); i++) {
		if (size < i*PTP_CANON_FolderEntryLen) break;
		ptp_unpack_Canon_FE(params,
				    data+i*PTP_CANON_FolderEntryLen,
				    &((*entries)[i]) );
	}

	free (data);
	return PTP_RC_OK;
error:
	free (data);
	return PTP_RC_GeneralError;
}

/**
 * ptp_canon_get_objecthandle_by_name:
 *
 * This command looks up the specified object on the camera.
 *
 * Format is "A:\\PATH".
 *
 * The 'A' is the VolumeLabel from GetStorageInfo,
 * my IXUS has "A" for the card and "V" for internal memory.
 *
 * params:	PTPParams*
 *      char* name - path name
 *
 * Return values: Some PTP_RC_* code.
 *      uint32_t *handle - PTP object handle.
 *
 **/
uint16_t
ptp_canon_get_objecthandle_by_name (PTPParams* params, char* name, uint32_t* handle)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint8_t		len = 0;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetObjectHandleByName);
	data = calloc (2,(strlen(name)+2));
	if (!data) return PTP_RC_GeneralError;
	ptp_pack_string (params, name, data, 0, &len);
	ret=ptp_transaction (params, &ptp, PTP_DP_SENDDATA, (len+1)*2+1, &data, NULL);
	free (data);
	*handle = ptp.Param1;
	return ret;
}

/**
 * ptp_canon_get_customize_data:
 *
 * This command downloads the specified theme slot, including jpegs
 * and wav files.
 *
 * params:	PTPParams*
 *      uint32_t themenr - nr of theme
 *
 * Return values: Some PTP_RC_* code.
 *      unsigned char **data - pointer to data pointer
 *      unsigned int  *size - size of data returned
 *
 **/
uint16_t
ptp_canon_get_customize_data (PTPParams* params, uint32_t themenr,
		unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_GetCustomizeData, themenr);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}


uint16_t
ptp_nikon_curve_download (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_CurveDownload);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_sony_sdioconnect:
 *
 * This changes modes of the camera
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_sony_sdioconnect (PTPParams* params, uint32_t phase, uint32_t p2, uint32_t p3)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_SDIO_Connect, phase, p2, p3);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, NULL));
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_sony_qx_connect:
 *
 * This changes modes of the camera
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_sony_qx_connect (PTPParams* params, uint32_t p1, uint32_t p2, uint32_t p3)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_QX_Connect, p1, p2, p3);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, NULL));
	free (data);
	return PTP_RC_OK;
}


/*
 * Old mode - From Sony docs: 'For Models Earlier than 2020'
 * - Notch up/down shutterpeed, iso and f-stop
 * - 16bit capture mode
 * - New cameras also support this mode (if requested)
 */
#define SONY_PROTOCOL_2_00 0x0c8

/*
 * New mode - From Sony docs: 'For 2020 Models or Later'
 * - Directly settable shutterpeed, iso and f-stop
 * - 32bit capture mode
 * - Many new properties
 */
#define SONY_PROTOCOL_3_00 0x12c

/**
 * ptp_sony_get_vendorpropcodes:
 *
 * This command downloads the vendor specific property codes.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *      unsigned char **data - pointer to data pointer
 *      unsigned int  *size - size of data returned
 *
 **/
uint16_t
ptp_sony_get_vendorpropcodes (PTPParams* params, uint16_t **props, unsigned int *size)
{
	PTPContainer	ptp;
	unsigned char	*xdata = NULL;
	unsigned int 	xsize, psize1 = 0, psize2 = 0, i, offset;
	uint16_t	*props1 = NULL,*props2 = NULL;

	*props = NULL;
	*size = 0;
	/* Request new protocol version - cameras that dont support it will return their max supported version */
	PTP_CNT_INIT(ptp, PTP_OC_SONY_SDIO_GetExtDeviceInfo, SONY_PROTOCOL_3_00, 1);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &xdata, &xsize));
	if (xsize == 0) {
		params->sony_mode_ver = 2;
		ptp_debug (params, "No special operations sent?");
		return PTP_RC_OK;
	}
	uint16_t version = dtoh16a(xdata);
	if (version == SONY_PROTOCOL_3_00) {
		params->sony_mode_ver = 3;
	} else {
		params->sony_mode_ver = 2;
	}

	ptp_debug (params, "camera version is %d", version);
	offset = 2;

	ptp_unpack_uint16_t_array (params, xdata, &offset, xsize, &props1, &psize1);
	ptp_debug (params, "device properties:");
	for (i=0;i<psize1;i++) {
		ptp_debug (params, "\t0x%04x", props1[i]);
	}
	ptp_unpack_uint16_t_array(params, xdata, &offset, xsize, &props2, &psize2);
	ptp_debug (params, "controls:");
	for (i=0;i<psize2;i++) {
		ptp_debug (params, "\t0x%04x",props2[i]);
	}
	free (xdata);

	*props = calloc(psize1 + psize2, sizeof(uint16_t));
	if (!*props) {
		free (props1);
		free (props2);
		return PTP_RC_GeneralError;
	}
	*size = psize1+psize2;
	memcpy (*props, props1, psize1*sizeof(uint16_t));
	memcpy ((*props)+psize1, props2, psize2*sizeof(uint16_t));
	free (props1);
	free (props2);
	return PTP_RC_OK;
}

uint16_t
ptp_sony_qx_get_vendorpropcodes (PTPParams* params, uint16_t **props, unsigned int *size)
{
	PTPContainer	ptp;
	unsigned char	*xdata = NULL;
	unsigned int 	xsize, psize1 = 0, psize2 = 0, offset = 0;
	uint16_t	*props1 = NULL,*props2 = NULL;

	*props = NULL;
	*size = 0;
	PTP_CNT_INIT(ptp, PTP_OC_SONY_QX_GetSDIOGetExtDeviceInfo, 0xc8 /* unclear */);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &xdata, &xsize));
	if (xsize == 0) {
		ptp_debug (params, "No special operations sent?");
		return PTP_RC_OK;
	}

	offset = 2;
	ptp_unpack_uint16_t_array (params, xdata, &offset, xsize, &props1, &psize1);
	ptp_unpack_uint16_t_array (params, xdata, &offset, xsize, &props2, &psize2);
	free (xdata);

	*props = calloc(psize1+psize2, sizeof(uint16_t));
	if (!*props) {
		free (props1);
		free (props2);
		return PTP_RC_GeneralError;
	}
	*size = psize1+psize2;
	memcpy (*props, props1, psize1*sizeof(uint16_t));
	memcpy ((*props)+psize1, props2, psize2*sizeof(uint16_t));
	free (props1);
	free (props2);
	return PTP_RC_OK;
}


uint16_t
ptp_sony_getdevicepropdesc (PTPParams* params, uint16_t propcode, PTPDevicePropDesc *dpd)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int 	size, len = 0;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_GetDevicePropdesc, propcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	/* first 16 bit is 0xc8 0x00, then an array of 16 bit PTP ids */
	ret = ptp_unpack_Sony_DPD(params,data,dpd,size,&len) ? PTP_RC_OK : PTP_ERROR_IO;
	free (data);
	return ret;
}

static uint16_t
_ptp_sony_getalldevicepropdesc (PTPParams* params, uint16_t opcode)
{
	PTPContainer		ptp;
	unsigned char		*data = NULL, *dpddata;
	unsigned int		size, readlen;
	PTPDevicePropDesc	dpd;
	time_t			now;

	ptp_debug (params, "_ptp_sony_getalldevicepropdesc: opcode %04x", opcode);
	/* for old A900 / A700 who does not have this, but has capture */
	if (!ptp_operation_issupported(params, opcode))
		return PTP_RC_OK;

	PTP_CNT_INIT(ptp, opcode);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (size <= 8) {
		free (data);
		return PTP_RC_GeneralError;
	}
	dpddata = data+8; /* nr of entries 32bit, 0 32bit */
	size -= 8;
	time(&now);
	while (size>0) {
		if (!ptp_unpack_Sony_DPD (params, dpddata, &dpd, size, &readlen))
			break;

		dpd.timestamp = now;
		uint16_t dpc = dpd.DevicePropCode;
		PTPDevicePropDesc* dpd_in_cache = ptp_find_dpd_in_cache(params, dpc);

		/* debug output to see what changes */
		if (dpd_in_cache) {
			switch (dpd.DataType) {
#define CHECK_CHANGED(type) \
				if (dpd_in_cache->CurrentValue.type != dpd.CurrentValue.type) \
					ptp_debug (params, "ptp_sony_getalldevicepropdesc: %s(%04x): value %d -> %d", \
					           ptp_get_property_description (params, dpc), dpc, dpd_in_cache->CurrentValue.type, dpd.CurrentValue.type);
			case PTP_DTC_INT8:   CHECK_CHANGED(i8); break;
			case PTP_DTC_UINT8:  CHECK_CHANGED(u8); break;
			case PTP_DTC_UINT16: CHECK_CHANGED(u16); break;
			case PTP_DTC_INT16:  CHECK_CHANGED(i16); break;
			case PTP_DTC_INT32:  CHECK_CHANGED(i32); break;
			case PTP_DTC_UINT32: CHECK_CHANGED(u32); break;
			default:
				break;
			}
		}

		if (!dpd_in_cache)
			array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
		else
			ptp_free_devicepropdesc (dpd_in_cache);
		move(*dpd_in_cache, dpd);
#if 0
		ptp_debug (params, "dpd.DevicePropCode %04x, readlen %d, getset %d", dpd.DevicePropCode, readlen, dpd.GetSet);
		switch (dpd.DataType) {
		case PTP_DTC_INT8:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.i8, dpd.CurrentValue.i8);
			break;
		case PTP_DTC_UINT8:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.u8, dpd.CurrentValue.u8);
			break;
		case PTP_DTC_UINT16:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.u16, dpd.CurrentValue.u16);
			break;
		case PTP_DTC_INT16:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.i16, dpd.CurrentValue.i16);
			break;
		case PTP_DTC_INT32:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.i32, dpd.CurrentValue.i32);
			break;
		case PTP_DTC_UINT32:
			ptp_debug (params, "value %d/%x", dpd.CurrentValue.u32, dpd.CurrentValue.u32);
			break;
		default:
			ptp_debug (params, "unknown type %x", dpd.DataType);
			break;
		}
#endif
		dpddata += readlen;
		size -= readlen;
	}
	free(data);
	return PTP_RC_OK;
}

uint16_t
ptp_sony_getalldevicepropdesc (PTPParams* params) {
	return _ptp_sony_getalldevicepropdesc (params, PTP_OC_SONY_SDIO_GetAllExtDevicePropInfo);
}

uint16_t
ptp_sony_qx_getalldevicepropdesc (PTPParams* params) {
	return _ptp_sony_getalldevicepropdesc (params, PTP_OC_SONY_QX_GetAllDevicePropData);
}

uint16_t
ptp_sony_setdevicecontrolvaluea (PTPParams* params, uint16_t propcode,
			PTPPropValue *value, uint16_t datatype)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_SDIO_SetExtDevicePropValue, propcode);
	size = ptp_pack_DPV(params, value, &data, datatype);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_sony_qx_setdevicecontrolvaluea (PTPParams* params, uint16_t propcode,
			PTPPropValue *value, uint16_t datatype)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_QX_SetControlDeviceA, propcode);
	size = ptp_pack_DPV(params, value, &data, datatype);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_sony_setdevicecontrolvalueb (PTPParams* params, uint16_t propcode,
			PTPPropValue *value, uint16_t datatype)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_SDIO_ControlDevice, propcode);
	size = ptp_pack_DPV(params, value, &data , datatype);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_sony_qx_setdevicecontrolvalueb (PTPParams* params, uint16_t propcode,
			PTPPropValue *value, uint16_t datatype)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_SONY_QX_SetControlDeviceB, propcode);
	size = ptp_pack_DPV(params, value, &data , datatype);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}


uint16_t
ptp_sony_9280 (PTPParams* params, uint32_t param1,
	uint32_t additional, uint32_t data2, uint32_t data3, uint32_t data4, uint8_t x, uint8_t y)
{
	PTPContainer	ptp;
	unsigned char 	buf[18];
	unsigned char*	data = buf;

	PTP_CNT_INIT(ptp, 0x9280, param1);

	if ((additional != 0) && (additional != 2))
		return PTP_RC_GeneralError;

	htod32a(&buf[0], additional);
	htod32a(&buf[4], data2);
	htod32a(&buf[8], data3);
	htod32a(&buf[12], data4);

	/* only sent in the case where additional is 2 */
	buf[16]= x; buf[17]= y;

	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, 16+additional, &data, NULL);
}

uint16_t
ptp_sony_9281 (PTPParams* params, uint32_t param1) {
	PTPContainer	ptp;
	unsigned int	size = 0;
	unsigned char	*buffer = NULL;

	PTP_CNT_INIT(ptp, 0x9281, param1);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &buffer, &size));
	free (buffer);
	return PTP_RC_OK;
}

static struct {
    uint16_t ControlCode;
    uint16_t Datatype;
    uint8_t FormFlag;
} sony_mode3_controls[] = {
	{ PTP_DPC_SONY_ShutterHalfRelease, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_ShutterRelease, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_AELButton, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_AFLButton, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_RequestOneShooting, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_MovieRecButtonHold, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_FELButton, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_ManualFocusAdjust, PTP_DTC_UINT16, PTP_DPFF_Range },
	{ PTP_DPC_SONY_AWBLButton, PTP_DTC_UINT16, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_AFAreaPosition, PTP_DTC_UINT32, PTP_DPFF_Range },
	{ PTP_DPC_SONY_ZoomOperation, PTP_DTC_INT8, PTP_DPFF_Range },
	{ PTP_DPC_SONY_SaveZoomAndFocusPosition, PTP_DTC_UINT8, PTP_DPFF_Enumeration },
	{ PTP_DPC_SONY_LoadZoomAndFocusPosition, PTP_DTC_UINT8, PTP_DPFF_Enumeration },
};

/**
 * ptp_generic_getdevicepropdesc:
 *
 * This command gets a propertydesc.
 * If a vendor specific property desc query is available, it uses that.
 * If not, it falls back to the generic PTP getdevicepropdesc.
 *
 * params:	PTPParams*
 *      uint16_t propcode
 *      PTPDevicePropDesc *dpd
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
/* Cache time in seconds. Should perhaps be more granular... */

uint16_t
ptp_generic_getdevicepropdesc (PTPParams *params, uint32_t propcode, PTPDevicePropDesc *dpd)
{
	unsigned int	i;
	time_t		now = time(NULL);

	PTPDevicePropDesc* dpd_in_cache = ptp_find_dpd_in_cache(params, propcode);

	if (!dpd_in_cache) {
		array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
	}

	if (dpd_in_cache->DataType != PTP_DTC_UNDEF) {
		if (dpd_in_cache->timestamp + params->cachetime > now) {
			duplicate_DevicePropDesc(dpd_in_cache, dpd);
			return PTP_RC_OK;
		}
		/* free cached entry as we will refetch it. */
		ptp_free_devicepropdesc (dpd_in_cache);
		dpd_in_cache = NULL;
	}

	/* Sony is handled directly here, also for "normal" properties */
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_SONY) &&
		ptp_operation_issupported(params, PTP_OC_SONY_SDIO_GetAllExtDevicePropInfo)
	) {
		CHECK_PTP_RC(ptp_sony_getalldevicepropdesc (params));

		dpd_in_cache = ptp_find_dpd_in_cache(params, propcode);
		if (!dpd_in_cache) {
			if (params->sony_mode_ver==3) {
				// Sony's GetAllExtDevicePropInfo API doesn't return some properties in mode 3.
				// These are referred to as 'Controls' in the Sony documentation, if the Control is
				// listed in PTPDeviceInfo.DeviceProps but not returned by SDIO_GetAllExtDevicePropInfo,
				// then add it here. Controls usually are toggle buttons, but some can be ranges of
				// values or even compound values.
				for (i=0;i<ARRAYSIZE(sony_mode3_controls);i++) {
					if (sony_mode3_controls[i].ControlCode == propcode) {
						array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
						dpd_in_cache->DevicePropCode = propcode;
						dpd_in_cache->DataType = sony_mode3_controls[i].Datatype;
						// Not so much get / but for sure set!
						dpd_in_cache->GetSet = PTP_DPGS_GetSet;
						dpd_in_cache->FormFlag = sony_mode3_controls[i].FormFlag;
						dpd_in_cache->timestamp = now;
						goto done;
					}
				}
			}

			ptp_debug (params, "alpha property 0x%04x not found?\n", propcode);
			return PTP_RC_GeneralError;
		}
		goto done;
	}
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_SONY) &&
		ptp_operation_issupported(params, PTP_OC_SONY_QX_GetAllDevicePropData)
	) {
		CHECK_PTP_RC(ptp_sony_qx_getalldevicepropdesc (params));

		dpd_in_cache = ptp_find_dpd_in_cache(params, propcode);
		if (!dpd_in_cache) {
			ptp_debug (params, "qx property 0x%04x not found?\n", propcode);
			return PTP_RC_GeneralError;
		}
		goto done;
	}
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_SONY) &&
		ptp_operation_issupported(params, PTP_OC_SONY_GetDevicePropdesc)
	) {
		PTPDevicePropDesc tmpdpd;

		CHECK_PTP_RC(ptp_sony_getdevicepropdesc (params, propcode, &tmpdpd));

		if (!dpd_in_cache)
			array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
		else
			ptp_free_devicepropdesc (dpd_in_cache);
		move(*dpd_in_cache, tmpdpd);
		goto done;
	}

	if (!ptp_is_vendor_extension_prop(propcode))
		goto generic;

	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
		ptp_operation_issupported(params, PTP_OC_CANON_EOS_RequestDevicePropValue)
	) {
		PTPDevicePropDesc *eos_dpd = ptp_find_eos_devicepropdesc(params, propcode);
		if (!eos_dpd) {
			ptp_debug (params, "Canon EOS property %04x not found", propcode);
			if ((propcode & 0xFF00) == 0xD100 || (propcode & 0xFF00) == 0xD200)
				return PTP_RC_DevicePropNotSupported;
			else
				goto generic;
		}
		if (!dpd_in_cache) {
			array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
		}
		duplicate_DevicePropDesc(eos_dpd, dpd_in_cache);
		goto done;
	}

generic:
	if (ptp_operation_issupported(params, PTP_OC_GetDevicePropDesc)) {
		PTPDevicePropDesc tmpdpd;

		CHECK_PTP_RC(ptp_getdevicepropdesc (params, propcode, &tmpdpd));
		if (!dpd_in_cache)
			array_push_back_empty(&params->dpd_cache, &dpd_in_cache);
		else
			ptp_free_devicepropdesc (dpd_in_cache);
		move(*dpd_in_cache, tmpdpd);
		goto done;
	}

	return PTP_RC_OperationNotSupported;

done:
	dpd_in_cache->timestamp = now;
	duplicate_DevicePropDesc(dpd_in_cache, dpd);
	return PTP_RC_OK;
}

/**
 * ptp_generic_setdevicepropvalue:
 *
 * This command sets a property value, device specific.
 *
 * params:	PTPParams*
 *      uint16_t propcode
 *      PTPDevicePropertyValue *value
 *      uint16_t datatype
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_generic_setdevicepropvalue (PTPParams* params, uint32_t propcode,
	PTPPropValue *value, uint16_t datatype)
{
	/* reset the cache entry */
	PTPDevicePropDesc *dpd_in_cache = ptp_find_dpd_in_cache(params, propcode);
	if (dpd_in_cache)
		dpd_in_cache->timestamp = 0;

	/* FIXME: change the cache? hmm */
	/* this works for some methods, but not for all */
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_SONY) &&
		ptp_operation_issupported(params, PTP_OC_SONY_SDIO_SetExtDevicePropValue)
	)
		return ptp_sony_setdevicecontrolvaluea (params, propcode, value, datatype);
	/* Sony QX method */
	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_SONY) &&
		ptp_operation_issupported(params, PTP_OC_SONY_QX_SetControlDeviceA)
	)
		return ptp_sony_qx_setdevicecontrolvaluea (params, propcode, value, datatype);
	return ptp_setdevicepropvalue (params, propcode, value, datatype);
}

/**
 * ptp_nikon_get_vendorpropcodes:
 *
 * This command downloads the vendor specific property codes.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *      unsigned char **data - pointer to data pointer
 *      unsigned int  *size - size of data returned
 *
 **/
uint16_t
ptp_nikon_get_vendorpropcodes (PTPParams* params, uint16_t **props, unsigned int *size)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	xsize = 0, offset = 0;

	*props = NULL;
	*size = 0;
	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetVendorPropCodes);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &xsize));
	ptp_unpack_uint16_t_array(params, data, &offset, xsize, props, size);
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_nikon_get_vendorcodes:
 *
 * This command downloads the vendor specific property codes.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *      unsigned char	**data - pointer to data pointer
 *      uint32_t 	**props - 32bit properties array
 *      unsigned int	*size - size of data returned in elements
 *
 **/
uint16_t
ptp_nikon_get_vendorcodes (PTPParams* params, uint32_t **props, unsigned int *size)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	xsize = 0, offset = 0;

	*props = NULL;
	*size = 0;
	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetVendorCodes, 0xd);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &xsize));
	ptp_unpack_uint32_t_array(params, data, &offset, xsize, props, size);
	free (data);
	return PTP_RC_OK;
}

uint16_t
ptp_nikon_getfileinfoinblock ( PTPParams* params,
	uint32_t p1, uint32_t p2, uint32_t p3,
	unsigned char **data, unsigned int *size
) {
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetFileInfoInBlock, p1, p2, p3);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_nikon_get_liveview_image:
 *
 * This command gets a LiveView image from newer Nikons DSLRs.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_get_liveview_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetLiveViewImg);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_nikon_get_preview_image:
 *
 * This command gets a Preview image from newer Nikons DSLRs.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_get_preview_image (PTPParams* params, unsigned char **xdata, unsigned int *xsize,
	uint32_t *handle)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetPreviewImg);

	/* FIXME:
	 * pdslrdashboard passes 3 parameters:
	 * objectid, minimum size, maximum size
	 */
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, xdata, xsize));
	if (ptp.Nparam > 0)
		*handle = ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_canon_eos_get_remotemode:
 *
 * This command gets the EOS remote mode.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_eos_getremotemode (PTPParams* params, uint32_t *mode)
{
	PTPContainer	ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetRemoteMode);

	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*mode = 0;
	if (ptp.Nparam > 0)
		*mode = ptp.Param1;
	return PTP_RC_OK;
}

/**
 * ptp_canon_eos_get_viewfinder_image:
 *
 * This command gets a Viewfinder image from newer Nikons DSLRs.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_canon_eos_get_viewfinder_image (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	/* Saw 3 arguments ... 0x00200000 for EOS1000D, also used 0x00100000 */
	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetViewFinderData, 0x00200000 /* from trace */, 0, 0);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

uint16_t
ptp_canon_eos_get_viewfinder_image_handler (PTPParams* params, PTPDataHandler*handler)
{
	PTPContainer ptp;

	/* Saw 3 arguments ... 0x00200000 for EOS1000D, also used 0x00100000 */
	PTP_CNT_INIT(ptp, PTP_OC_CANON_EOS_GetViewFinderData, 0x00200000 /* from trace */, 0, 0);
	return ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, handler);
}

/**
 * ptp_nikon_check_event:
 *
 * This command checks the event queue on the Nikon.
 *
 * params:	PTPParams*
 *      PTPUSBEventContainer **event - list of usb events.
 *	int *evtcnt - number of usb events in event structure.
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_check_event (PTPParams* params, PTPContainer** event, unsigned int* evtcnt)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetEvent);
	*evtcnt = 0;
	CHECK_PTP_RC(ptp_transaction (params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_Nikon_EC (params, data, size, event, evtcnt);
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_nikon_check_eventex:
 *
 * This command checks the event queue on the Nikon.
 *
 * params:	PTPParams*
 *      PTPUSBEventContainer **event - list of usb events.
 *	int *evtcnt - number of usb events in event structure.
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_check_eventex (PTPParams* params, PTPContainer** event, unsigned int* evtcnt)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetEventEx);
	*evtcnt = 0;
	CHECK_PTP_RC(ptp_transaction (params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_Nikon_EC_EX (params, data, size, event, evtcnt);
	free (data);
	return PTP_RC_OK;
}

/**
 * ptp_nikon_getptpipinfo:
 *
 * This command gets the ptpip info data.
 *
 * params:	PTPParams*
 *	unsigned char *data	- data
 *	unsigned int size	- size of returned data
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_getptpipinfo (PTPParams* params, unsigned char **data, unsigned int *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetDevicePTPIPInfo);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
}

/**
 * ptp_nikon_getwifiprofilelist:
 *
 * This command gets the wifi profile list.
 *
 * params:	PTPParams*
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_getwifiprofilelist (PTPParams* params)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size, pos = 0, profn, n;
	char		*buffer;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_GetProfileAllData);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 2)
		goto error;

	params->wifi_profiles_version = dtoh8o(data, pos);
	params->wifi_profiles_number  = dtoh8o(data, pos);
	free(params->wifi_profiles);

	params->wifi_profiles = calloc(params->wifi_profiles_number,sizeof(PTPNIKONWifiProfile));
	if (!params->wifi_profiles)
		goto error;

	profn = 0;
	while (profn < params->wifi_profiles_number && pos < size) {
		if (pos+6 >= size)
			goto error;
		params->wifi_profiles[profn].id    = dtoh8o(data, pos);
		params->wifi_profiles[profn].valid = dtoh8o(data, pos);

		n = dtoh32o(data, pos);
		if (pos+n+4 >= size)
			goto error;
		strncpy(params->wifi_profiles[profn].profile_name, (char*)&data[pos], n);
		params->wifi_profiles[profn].profile_name[16] = '\0';
		pos += n;

		params->wifi_profiles[profn].display_order = dtoh8o(data, pos);
		params->wifi_profiles[profn].device_type   = dtoh8o(data, pos);
		params->wifi_profiles[profn].icon_type     = dtoh8o(data, pos);

		if (!ptp_unpack_string(params, data, &pos, size, &buffer))
			goto error;
		strncpy(params->wifi_profiles[profn].creation_date, buffer, sizeof(params->wifi_profiles[profn].creation_date));
		free (buffer);
		if (pos+1 >= size)
			goto error;
		/* FIXME: check if it is really last usage date */
		if (!ptp_unpack_string(params, data, &pos, size, &buffer))
			goto error;
		strncpy(params->wifi_profiles[profn].lastusage_date, buffer, sizeof(params->wifi_profiles[profn].lastusage_date));
		free (buffer);
		if (pos+5 >= size)
			goto error;

		n = dtoh32o(data, pos);
		if (pos+n >= size)
			goto error;
		strncpy(params->wifi_profiles[profn].essid, (char*)data + pos, n);
		params->wifi_profiles[profn].essid[32] = '\0';
		pos += n;
		pos += 1;
		profn++;
	}

#if 0
	PTPNIKONWifiProfile test;
	memset(&test, 0, sizeof(PTPNIKONWifiProfile));
	strcpy(test.profile_name, "MyTest");
	test.icon_type = 1;
	strcpy(test.essid, "nikon");
	test.ip_address = 10 + 11 << 16 + 11 << 24;
	test.subnet_mask = 24;
	test.access_mode = 1;
	test.wifi_channel = 1;
	test.key_nr = 1;

	ptp_nikon_writewifiprofile(params, &test);
#endif
	/* everything went Ok */
	free(data);
	return PTP_RC_OK;
error:
	free(data);
	return PTP_RC_GeneralError;
}

/**
 * ptp_nikon_writewifiprofile:
 *
 * This command gets the ptpip info data.
 *
 * params:	PTPParams*
 *	unsigned int profilenr	- profile number
 *	unsigned char *data	- data
 *	unsigned int size	- size of returned data
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_nikon_writewifiprofile (PTPParams* params, PTPNIKONWifiProfile* profile)
{
	PTPContainer ptp;
	unsigned char buffer[1024];
	unsigned char* data = buffer;
	int size = 0;
	int i;
	uint8_t len;
	int profilenr = -1;
	unsigned char guid[16];

	ptp_nikon_getptpipguid(guid);

	if (!params->wifi_profiles)
		CHECK_PTP_RC(ptp_nikon_getwifiprofilelist(params));

	for (i = 0; i < params->wifi_profiles_number; i++) {
		if (!params->wifi_profiles[i].valid) {
			profilenr = params->wifi_profiles[i].id;
			break;
		}
	}

	if (profilenr == -1) {
		/* No free profile! */
		return PTP_RC_StoreFull;
	}

	memset(buffer, 0, 1024);

	buffer[0x00] = 0x64; /* Version */

	/* Profile name */
	htod32a(&buffer[0x01], 17);
	/* 16 as third parameter, so there will always be a null-byte in the end */
	strncpy((char*)&buffer[0x05], profile->profile_name, 16);

	buffer[0x16] = 0x00; /* Display order */
	buffer[0x17] = profile->device_type;
	buffer[0x18] = profile->icon_type;

	/* FIXME: Creation date: put a real date here */
	ptp_pack_string(params, "19990909T090909", data, 0x19, &len);

	/* IP parameters */
	memcpy(&buffer[0x3A],&profile->ip_address,sizeof(profile->ip_address));
	/**((unsigned int*)&buffer[0x3A]) = profile->ip_address; *//* Do not reverse bytes */
	buffer[0x3E] = profile->subnet_mask;
	memcpy(&buffer[0x3F],&profile->gateway_address,sizeof(profile->gateway_address));
	/**((unsigned int*)&buffer[0x3F]) = profile->gateway_address; */ /* Do not reverse bytes */
	buffer[0x43] = profile->address_mode;

	/* Wifi parameters */
	buffer[0x44] = profile->access_mode;
	buffer[0x45] = profile->wifi_channel;

	htod32a(&buffer[0x46], 33); /* essid */
	 /* 32 as third parameter, so there will always be a null-byte in the end */
	strncpy((char*)&buffer[0x4A], profile->essid, 32);

	buffer[0x6B] = profile->authentification;
	buffer[0x6C] = profile->encryption;
	htod32a(&buffer[0x6D], 64);
	for (i = 0; i < 64; i++) {
		buffer[0x71+i] = profile->key[i];
	}
	buffer[0xB1] = profile->key_nr;
	memcpy(&buffer[0xB2], guid, 16);

	switch(profile->encryption) {
	case 1: /* WEP 64bit */
		htod16a(&buffer[0xC2], 5); /* (64-24)/8 = 5 */
		break;
	case 2: /* WEP 128bit */
		htod16a(&buffer[0xC2], 13); /* (128-24)/8 = 13 */
		break;
	default:
		htod16a(&buffer[0xC2], 0);
	}
	size = 0xC4;

	PTP_CNT_INIT(ptp, PTP_OC_NIKON_SendProfileData, profilenr);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
}

/**
 * ptp_mtp_getobjectpropssupported:
 *
 * This command gets the object properties possible from the device.
 *
 * params:	PTPParams*
 *	uint ofc		- object format code
 *	unsigned int *propnum	- number of elements in returned array
 *	uint16_t *props		- array of supported properties
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_mtp_getobjectpropssupported (PTPParams* params, uint16_t ofc,
		 uint32_t *propnum, uint16_t **props
) {
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	xsize = 0, offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_GetObjectPropsSupported, ofc);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &xsize));
	if (!data) return PTP_RC_GeneralError;
	ptp_unpack_uint16_t_array (params, data, &offset, xsize, props, propnum);
	free(data);
	return PTP_RC_OK;
}

/**
 * ptp_mtp_getobjectpropdesc:
 *
 * This command gets the object property description.
 *
 * params:	PTPParams*
 *	uint16_t opc	- object property code
 *	uint16_t ofc	- object format code
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_mtp_getobjectpropdesc (
	PTPParams* params, uint16_t opc, uint16_t ofc, PTPObjectPropDesc *opd
) {
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_GetObjectPropDesc, opc, ofc);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_unpack_OPD (params, data, opd, size);
	free(data);
	return PTP_RC_OK;
}

/**
 * ptp_mtp_getobjectpropvalue:
 *
 * This command gets the object properties of an object handle.
 *
 * params:	PTPParams*
 *	uint32_t handle	- object handle
 *	uint16_t opc	- object prop code
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_mtp_getobjectpropvalue (
	PTPParams* params, uint32_t handle, uint16_t opc,
	PTPPropValue *value, uint16_t datatype
) {
	PTPContainer	ptp;
	uint16_t	ret = PTP_RC_OK;
	unsigned char	*data = NULL;
	unsigned int	size, offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_GetObjectPropValue, handle, opc);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	if (!ptp_unpack_DPV(params, data, &offset, size, value, datatype)) {
		ptp_debug (params, "ptp_mtp_getobjectpropvalue: unpacking DPV failed");
		ret = PTP_RC_GeneralError;
	}
	free(data);
	return ret;
}

/**
 * ptp_mtp_setobjectpropvalue:
 *
 * This command gets the object properties of an object handle.
 *
 * params:	PTPParams*
 *	uint32_t handle	- object handle
 *	uint16_t opc	- object prop code
 *
 * Return values: Some PTP_RC_* code.
 *
 **/
uint16_t
ptp_mtp_setobjectpropvalue (
	PTPParams* params, uint32_t handle, uint16_t opc,
	PTPPropValue *value, uint16_t datatype
) {
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_SetObjectPropValue, handle, opc);
	size = ptp_pack_DPV(params, value, &data, datatype);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_mtp_getobjectreferences (PTPParams* params, uint32_t handle, uint32_t** ohArray, uint32_t* arraylen)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	unsigned int	size;
	uint32_t	offset = 0;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_GetObjectReferences, handle);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data , &size));
	ret = ptp_unpack_uint32_t_array(params, data , &offset, size, ohArray, arraylen) ? PTP_RC_OK : PTP_ERROR_IO;
	free(data);
	return ret;
}

uint16_t
ptp_mtp_setobjectreferences (PTPParams* params, uint32_t handle, uint32_t* ohArray, uint32_t arraylen)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_SetObjectReferences, handle);
	size = ptp_pack_uint32_t_array(params, ohArray, arraylen, &data);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_mtp_getobjectproplist_generic (PTPParams* params, uint32_t handle, uint32_t formats, uint32_t properties,
	uint32_t propertygroups, uint32_t level, MTPObjectProp **props, int *nrofprops)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_GetObjPropList, handle, formats, properties, propertygroups, level);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	*nrofprops = ptp_unpack_OPL(params, data, props, size);
	free(data);
	return PTP_RC_OK;
}

uint16_t
ptp_mtp_getobjectproplist_level (PTPParams* params, uint32_t handle, uint32_t level, MTPObjectProp **props, int *nrofprops)
{
	return ptp_mtp_getobjectproplist_generic (params, handle,
		0x00000000U,  /* 0x00000000U should be "all formats" */
		0xFFFFFFFFU,  /* 0xFFFFFFFFU should be "all properties" */
		0,
		level,
		props,
		nrofprops
	);
}


uint16_t
ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPObjectProp **props, int *nrofprops)
{
	return ptp_mtp_getobjectproplist_level(params, handle, 0xFFFFFFFFU, props, nrofprops);
}

uint16_t
ptp_mtp_getobjectproplist_single (PTPParams* params, uint32_t handle, MTPObjectProps *props)
{
	return ptp_mtp_getobjectproplist_level(params, handle, 0, &props->val, (int*)&props->len);
}

uint16_t
ptp_mtp_sendobjectproplist (PTPParams* params, uint32_t* store, uint32_t* parenthandle, uint32_t* handle,
			    uint16_t objecttype, uint64_t objectsize, MTPObjectProp *props, int nrofprops)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_SendObjectPropList, *store, *parenthandle, (uint32_t) objecttype,
		     (uint32_t) (objectsize >> 32), (uint32_t) (objectsize & 0xffffffffU)
	);

	/* Set object handle to 0 for a new object */
	size = ptp_pack_OPL(params,props,nrofprops,&data);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	*store = ptp.Param1;
	*parenthandle = ptp.Param2;
	*handle = ptp.Param3;

	return ret;
}

uint16_t
ptp_mtp_setobjectproplist (PTPParams* params, MTPObjectProp *props, int nrofprops)
{
	PTPContainer	ptp;
	uint16_t	ret;
	unsigned char	*data = NULL;
	uint32_t	size;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_SetObjPropList);
	size = ptp_pack_OPL(params,props,nrofprops,&data);
	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
	free(data);
	return ret;
}

uint16_t
ptp_mtpz_sendwmdrmpdapprequest (PTPParams* params, unsigned char *appcertmsg, uint32_t size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_WMDRMPD_SendWMDRMPDAppRequest);
	return ptp_transaction (params, &ptp, PTP_DP_SENDDATA, size, &appcertmsg, NULL);
}

uint16_t
ptp_mtpz_getwmdrmpdappresponse (PTPParams* params, unsigned char **response, uint32_t *size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_MTP_WMDRMPD_GetWMDRMPDAppResponse);
	return ptp_transaction (params, &ptp, PTP_DP_GETDATA, 0, response, size);
}

/****** CHDK interface ******/

uint16_t
ptp_chdk_get_memory(PTPParams* params, int start, int num, unsigned char **buf)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_GetMemory, start, num);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, buf, NULL);
}

uint16_t
ptp_chdk_set_memory_long(PTPParams* params, int addr, int val)
{
	PTPContainer ptp;
	unsigned char *buf = (unsigned char *) &val; /* FIXME ... endianness? */

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_SetMemory, addr, 4);
	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, 4, &buf, NULL);
}

uint16_t
ptp_chdk_download(PTPParams* params, char *remote_fn, PTPDataHandler *handler)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_TempData, 0);
	CHECK_PTP_RC (ptp_transaction(params, &ptp, PTP_DP_SENDDATA, strlen(remote_fn), (unsigned char**)&remote_fn, NULL));

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_DownloadFile);
	return ptp_transaction_new (params, &ptp, PTP_DP_GETDATA, 0, handler);
}

#if 0
int ptp_chdk_upload(PTPParams* params, char *local_fn, char *remote_fn)
{
	uint16_t ret;
	PTPContainer ptp;
	char *buf = NULL;
	FILE *f;
	unsigned file_len, data_len, file_name_len;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_UploadFile);

	f = fopen(local_fn, "rb");
	if (f == NULL)
	{
		ptp_error(params, "could not open file \'%s\'", local_fn);
		return 0;
	}

	fseek(f, 0, SEEK_END);
	file_len = ftell(f);
	fseek(f, 0, SEEK_SET);

	file_name_len = strlen(remote_fn);
	data_len = 4 + file_name_len + file_len;
	buf = malloc(data_len);
	memcpy(buf, &file_name_len, 4);
	memcpy(buf+4, remote_fn, file_name_len);
	fread(buf+4+file_name_len, 1, file_len, f);

	fclose(f);

	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, data_len, &buf, NULL);

	free(buf);

	if (ret != PTP_RC_OK)
	{
		ptp_error(params, "unexpected return code 0x%x", ret);
		return 0;
	}
	return 1;
}

#endif

/*
 * Preliminary remote capture over USB code. Corresponding CHDK code is in the ptp-remote-capture-test
 * This is under development and should not be included in builds for general distribution
 */
/*
 * isready: 0: not ready, lowest 2 bits: available image formats, 0x10000000: error
 */
uint16_t
ptp_chdk_rcisready(PTPParams* params, int *isready, int *imgnum)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_RemoteCaptureIsReady);
	*isready = *imgnum = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*isready=ptp.Param1;
	*imgnum=ptp.Param2;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_rcgetchunk(PTPParams* params, int fmt, ptp_chdk_rc_chunk *chunk)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_RemoteCaptureGetData, fmt); //get chunk

	chunk->data = NULL;
	chunk->size = 0;
	chunk->offset = 0;
	chunk->last = 0;
	// TODO should allow ptp_getdata_transaction to send chunks directly to file, or to mem
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &chunk->data, NULL));
	chunk->size = ptp.Param1;
	chunk->last = (ptp.Param2 == 0);
	chunk->offset = ptp.Param3; //-1 for none
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_exec_lua(PTPParams* params, char *script, int flags, int *script_id, int *status)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_ExecuteScript, PTP_CHDK_SL_LUA | flags);
	*script_id = 0;
	*status = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_SENDDATA, strlen(script)+1, (unsigned char**)&script, NULL));
	*script_id = ptp.Param1;
	*status = ptp.Param2;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_get_version(PTPParams* params, int *major, int *minor)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_Version);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*major = ptp.Param1;
	*minor = ptp.Param2;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_get_script_status(PTPParams* params, unsigned *status)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_ScriptStatus);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*status = ptp.Param1;
	return PTP_RC_OK;
}
uint16_t
ptp_chdk_get_script_support(PTPParams* params, unsigned *status)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_ScriptSupport);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL));
	*status = ptp.Param1;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_write_script_msg(PTPParams* params, char *data, unsigned size, int target_script_id, int *status)
{
	PTPContainer ptp;

	// a zero length data phase appears to do bad things, camera stops responding to PTP
	if(!size) {
		ptp_error(params,"zero length message not allowed");
		*status = 0;
		return PTP_ERROR_BADPARAM;
	}
	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_WriteScriptMsg, target_script_id);
	*status = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, (unsigned char**)&data, NULL));
	*status = ptp.Param1;
	return PTP_RC_OK;
}
uint16_t
ptp_chdk_read_script_msg(PTPParams* params, ptp_chdk_script_msg **msg)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_ReadScriptMsg);

	*msg = NULL;

	/* camera will always send data, otherwise getdata will cause problems */
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, NULL));
	if (!data) {
		ptp_error(params,"no data received");
		return PTP_ERROR_BADPARAM;
	}

	/* for convenience, always allocate an extra byte and null it*/
	*msg = malloc(sizeof(ptp_chdk_script_msg) + ptp.Param4 + 1);
	(*msg)->type = ptp.Param1;
	(*msg)->subtype = ptp.Param2;
	(*msg)->script_id = ptp.Param3;
	(*msg)->size = ptp.Param4;
	memcpy((*msg)->data,data,(*msg)->size);
	(*msg)->data[(*msg)->size] = 0;
	free(data);
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_get_live_data(PTPParams* params, unsigned flags, unsigned char **data, unsigned int *data_size)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_GetDisplayData, flags);
	*data_size = 0;
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, NULL));
	*data_size = ptp.Param1;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_call_function(PTPParams* params, int *args, int size, int *ret)
{
	PTPContainer ptp;

	PTP_CNT_INIT(ptp, PTP_OC_CHDK, PTP_CHDK_CallFunction);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size*sizeof(int), (unsigned char **) &args, NULL));
	if (ret)
		*ret = ptp.Param1;
	return PTP_RC_OK;
}

uint16_t
ptp_chdk_parse_live_data (PTPParams* params, unsigned char *data, unsigned int data_size,
			  lv_data_header *header,
			  lv_framebuffer_desc *vpd, lv_framebuffer_desc *bmd
) {
	unsigned int byte_w;

	if (data_size < sizeof (*header))
		return PTP_ERROR_IO;
	ptp_unpack_chdk_lv_data_header (params, data, header);
	if (data_size < (header->vp_desc_start + sizeof (*vpd)) || data_size < (header->bm_desc_start + sizeof (*bmd)))
		return PTP_ERROR_IO;
	ptp_unpack_chdk_lv_framebuffer_desc (params, data+header->vp_desc_start, vpd);
	ptp_unpack_chdk_lv_framebuffer_desc (params, data+header->bm_desc_start, bmd);

	/* The buffer_width field corresponds to the number of Y values in a row,
	 * so the actual number of bytes would be either one and a half times
	 * or (for Digic 6 cameras) twice so large */
	byte_w = (vpd->fb_type == LV_FB_YUV8) ? vpd->buffer_width * 1.5 : vpd->buffer_width * 2;
	if (data_size < (vpd->data_start + (byte_w * vpd->visible_height)))
		return PTP_ERROR_IO;
	return PTP_RC_OK;
}


/**
 * Android MTP Extensions
 */

/**
 * ptp_android_getpartialobject64:
 * params:	PTPParams*
 *		handle			- Object handle
 *		offset			- Offset into object
 *		maxbytes		- Maximum of bytes to read
 *		object			- pointer to data area
 *		len			- pointer to returned length
 *
 * Get object 'handle' from device and store the data in newly
 * allocated 'object'. Start from offset and read at most maxbytes.
 *
 * This is a 64bit offset version of the standard GetPartialObject.
 *
 * Return values: Some PTP_RC_* code.
 **/
uint16_t
ptp_android_getpartialobject64 (PTPParams* params, uint32_t handle, uint64_t offset,
				uint32_t maxbytes, unsigned char** object,
				uint32_t *len)
{
	PTPContainer ptp;

	/* casts due to varargs otherwise pushing 64bit values on the stack */
	PTP_CNT_INIT(ptp, PTP_OC_ANDROID_GetPartialObject64, handle, ((uint32_t)offset & 0xFFFFFFFF), (uint32_t)(offset >> 32), maxbytes);
	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, len);
}

uint16_t
ptp_android_sendpartialobject (PTPParams* params, uint32_t handle, uint64_t offset,
				unsigned char* object,	uint32_t len)
{
	PTPContainer	ptp;
	uint16_t	ret;

	PTP_CNT_INIT(ptp, PTP_OC_ANDROID_SendPartialObject, handle, (uint32_t)(offset & 0xFFFFFFFF), (uint32_t)(offset >> 32), len);

	/*
	 * MtpServer.cpp is buggy: it uses write() without offset
	 * rather than pwrite to send the data for data coming with
	 * the header packet
	 */
	params->split_header_data = 1;
	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, len, &object, NULL);
	params->split_header_data = 0;

	return ret;
}

uint16_t
ptp_fuji_getdeviceinfo (PTPParams* params, uint16_t **props, unsigned int *numprops)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	nums, i, offset = 0, size  = 0;

	PTP_CNT_INIT(ptp, PTP_OC_FUJI_GetDeviceInfo);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));

	if (size < 8)
		goto error;

	nums = dtoh32o(data, offset);

	*props = calloc(nums, sizeof(uint16_t));
	if (!*props)
		goto error;
	*numprops = nums;
	for (i=0;i<nums;i++) {
		PTPDevicePropDesc	dpd;
		unsigned int		dsize = dtoh32o(data, offset);

		if (!ptp_unpack_DPD(params, data, &dpd, dsize, &offset))
			break;
		(*props)[i] = dpd.DevicePropCode;
	}
	free (data);
	return PTP_RC_OK;
error:
	free (data);
	return PTP_RC_GeneralError;
}

uint16_t
ptp_fuji_getevents (PTPParams* params, uint16_t** events, uint16_t* count)
{
	PTPContainer	ptp;
	unsigned char	*data = NULL;
	unsigned int	size = 0;

	PTP_CNT_INIT(ptp, PTP_OC_GetDevicePropValue, 0xd212);
	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
	ptp_debug(params, "ptp_fuji_getevents");
	*count = 0;
	if(size >= 2)
	{
		*count = dtoh16a(data);
		ptp_debug(params, "event count: %d", *count);
		*events = calloc(*count, sizeof(uint16_t));
		if(size >= 2u + *count * 6)
		{
			uint16_t	param;
			uint32_t	value;
			int		i;

			for(i = 0; i < *count; i++)
			{
				param = dtoh16a(&data[2 + 6 * i]);
				value = dtoh32a(&data[2 + 6 * i + 2]);
				(*events)[i] = param;
				ptp_debug(params, "param: %02x, value: %d ", param, value);

				/* reset the property cache entry for refetch ... */
				PTPDevicePropDesc *dpd_in_cache = ptp_find_dpd_in_cache(params, param);
				if (dpd_in_cache)
					dpd_in_cache->timestamp = 0;
			}
		}
	}
	free(data);
	return PTP_RC_OK;
}


/* Non PTP protocol functions */
/* devinfo testing functions */

int
ptp_event_issupported(PTPParams* params, uint16_t event)
{
	unsigned int i=0;

	for (;i<params->deviceinfo.Events_len;i++) {
		if (params->deviceinfo.Events[i]==event)
			return 1;
	}
	return 0;
}


int
ptp_property_issupported(PTPParams* params, uint16_t property)
{
	unsigned int i;

	for (i=0;i<params->deviceinfo.DeviceProps_len;i++)
		if (params->deviceinfo.DeviceProps[i]==property)
			return 1;
	return 0;
}

void
ptp_free_deviceinfo (PTPDeviceInfo *di)
{
	if (!di) return;
	free (di->SerialNumber);
	free (di->DeviceVersion);
	free (di->Model);
	free (di->Manufacturer);
	free (di->ImageFormats);
	free (di->CaptureFormats);
	free (di->VendorExtensionDesc);
	free (di->Operations);
	free (di->Events);
	free (di->DeviceProps);
	memset(di, 0, sizeof(*di));
}

void
ptp_free_objectinfo (PTPObjectInfo *oi)
{
	if (!oi) return;
	free (oi->Filename); oi->Filename = NULL;
	free (oi->Keywords); oi->Keywords = NULL;
}

void
ptp_free_object (PTPObject *ob)
{
	if (!ob) return;

	ptp_free_objectinfo (&ob->oi);
	free_array_recusive (&ob->mtp_props, ptp_free_object_prop);
	ob->flags = 0;
}

/* PTP error descriptions */
static struct {
	uint16_t rc;
	uint16_t vendor;
	const char *txt;
} ptp_errors[] = {
	{PTP_RC_Undefined,		0, N_("PTP Undefined Error")},
	{PTP_RC_OK,			0, N_("PTP OK!")},
	{PTP_RC_GeneralError,		0, N_("PTP General Error")},
	{PTP_RC_SessionNotOpen,		0, N_("PTP Session Not Open")},
	{PTP_RC_InvalidTransactionID,	0, N_("PTP Invalid Transaction ID")},
	{PTP_RC_OperationNotSupported,	0, N_("PTP Operation Not Supported")},
	{PTP_RC_ParameterNotSupported,	0, N_("PTP Parameter Not Supported")},
	{PTP_RC_IncompleteTransfer,	0, N_("PTP Incomplete Transfer")},
	{PTP_RC_InvalidStorageId,	0, N_("PTP Invalid Storage ID")},
	{PTP_RC_InvalidObjectHandle,	0, N_("PTP Invalid Object Handle")},
	{PTP_RC_DevicePropNotSupported,	0, N_("PTP Device Prop Not Supported")},
	{PTP_RC_InvalidObjectFormatCode,0, N_("PTP Invalid Object Format Code")},
	{PTP_RC_StoreFull,		0, N_("PTP Store Full")},
	{PTP_RC_ObjectWriteProtected,	0, N_("PTP Object Write Protected")},
	{PTP_RC_StoreReadOnly,		0, N_("PTP Store Read Only")},
	{PTP_RC_AccessDenied,		0, N_("PTP Access Denied")},
	{PTP_RC_NoThumbnailPresent,	0, N_("PTP No Thumbnail Present")},
	{PTP_RC_SelfTestFailed,		0, N_("PTP Self Test Failed")},
	{PTP_RC_PartialDeletion,	0, N_("PTP Partial Deletion")},
	{PTP_RC_StoreNotAvailable,	0, N_("PTP Store Not Available")},
	{PTP_RC_SpecificationByFormatUnsupported, 0, N_("PTP Specification By Format Unsupported")},
	{PTP_RC_NoValidObjectInfo,	0, N_("PTP No Valid Object Info")},
	{PTP_RC_InvalidCodeFormat,	0, N_("PTP Invalid Code Format")},
	{PTP_RC_UnknownVendorCode,	0, N_("PTP Unknown Vendor Code")},
	{PTP_RC_CaptureAlreadyTerminated, 0, N_("PTP Capture Already Terminated")},
	{PTP_RC_DeviceBusy,		0, N_("PTP Device Busy")},
	{PTP_RC_InvalidParentObject,	0, N_("PTP Invalid Parent Object")},
	{PTP_RC_InvalidDevicePropFormat,0, N_("PTP Invalid Device Prop Format")},
	{PTP_RC_InvalidDevicePropValue,	0, N_("PTP Invalid Device Prop Value")},
	{PTP_RC_InvalidParameter,	0, N_("PTP Invalid Parameter")},
	{PTP_RC_SessionAlreadyOpened,	0, N_("PTP Session Already Opened")},
	{PTP_RC_TransactionCanceled,	0, N_("PTP Transaction Canceled")},
	{PTP_RC_SpecificationOfDestinationUnsupported, 0, N_("PTP Specification Of Destination Unsupported")},
	{PTP_RC_InvalidEnumHandle,	0, N_("Invalid Enum Handle")},
	{PTP_RC_NoStreamEnabled,	0, N_("No Stream Enabled")},
	{PTP_RC_InvalidDataSet,		0, N_("Invalid Data Set")},

	{PTP_RC_EK_FilenameRequired,	PTP_VENDOR_EASTMAN_KODAK, N_("Filename Required")},
	{PTP_RC_EK_FilenameConflicts,	PTP_VENDOR_EASTMAN_KODAK, N_("Filename Conflicts")},
	{PTP_RC_EK_FilenameInvalid,	PTP_VENDOR_EASTMAN_KODAK, N_("Filename Invalid")},

	{PTP_RC_NIKON_HardwareError,			PTP_VENDOR_NIKON, N_("Hardware Error")},
	{PTP_RC_NIKON_OutOfFocus,			PTP_VENDOR_NIKON, N_("Out of Focus")},
	{PTP_RC_NIKON_ChangeCameraModeFailed,		PTP_VENDOR_NIKON, N_("Change Camera Mode Failed")},
	{PTP_RC_NIKON_InvalidStatus,			PTP_VENDOR_NIKON, N_("Invalid Status")},
	{PTP_RC_NIKON_SetPropertyNotSupported,		PTP_VENDOR_NIKON, N_("Set Property Not Supported")},
	{PTP_RC_NIKON_WbResetError,			PTP_VENDOR_NIKON, N_("Whitebalance Reset Error")},
	{PTP_RC_NIKON_DustReferenceError,		PTP_VENDOR_NIKON, N_("Dust Reference Error")},
	{PTP_RC_NIKON_ShutterSpeedBulb,			PTP_VENDOR_NIKON, N_("Shutter Speed Bulb")},
	{PTP_RC_NIKON_MirrorUpSequence,			PTP_VENDOR_NIKON, N_("Mirror Up Sequence")},
	{PTP_RC_NIKON_CameraModeNotAdjustFNumber,	PTP_VENDOR_NIKON, N_("Camera Mode Not Adjust FNumber")},
	{PTP_RC_NIKON_NotLiveView,			PTP_VENDOR_NIKON, N_("Not in Liveview")},
	{PTP_RC_NIKON_MfDriveStepEnd,			PTP_VENDOR_NIKON, N_("Mf Drive Step End")},
	{PTP_RC_NIKON_MfDriveStepInsufficiency,		PTP_VENDOR_NIKON, N_("Mf Drive Step Insufficiency")},
	{PTP_RC_NIKON_AdvancedTransferCancel,		PTP_VENDOR_NIKON, N_("Advanced Transfer Cancel")},
	{PTP_RC_NIKON_NoFullHDPresent,			PTP_VENDOR_NIKON, N_("No Full HD Present")},
	{PTP_RC_NIKON_StoreError,			PTP_VENDOR_NIKON, N_("Store Error")},
	{PTP_RC_NIKON_StoreUnformatted,			PTP_VENDOR_NIKON, N_("Store Unformatted")},
	{PTP_RC_NIKON_Bulb_Release_Busy,		PTP_VENDOR_NIKON, N_("Bulb Release Busy")},
	{PTP_RC_NIKON_Silent_Release_Busy,		PTP_VENDOR_NIKON, N_("Silent Release Busy")},
	{PTP_RC_NIKON_MovieFrame_Release_Busy,		PTP_VENDOR_NIKON, N_("MovieFrame Release Busy")},
	{PTP_RC_NIKON_Shutter_Speed_Time,		PTP_VENDOR_NIKON, N_("Shutter Speed Time")},
	{PTP_RC_NIKON_Waiting_2ndRelease,		PTP_VENDOR_NIKON, N_("Waiting for 2nd Release")},
	{PTP_RC_NIKON_MirrorUpCapture_Already_Start,	PTP_VENDOR_NIKON, N_("MirrorUpCapture Already Started")},
	{PTP_RC_NIKON_Invalid_SBAttribute_Value,	PTP_VENDOR_NIKON, N_("Invalid SBAttribute Value")},

	{PTP_RC_CANON_UNKNOWN_COMMAND,			PTP_VENDOR_CANON, N_("Unknown Command")},
	{PTP_RC_CANON_OPERATION_REFUSED,		PTP_VENDOR_CANON, N_("Operation Refused")},
	{PTP_RC_CANON_LENS_COVER,			PTP_VENDOR_CANON, N_("Lens Cover Present")},
	{PTP_RC_CANON_BATTERY_LOW,			PTP_VENDOR_CANON, N_("Battery Low")},
	{PTP_RC_CANON_NOT_READY,			PTP_VENDOR_CANON, N_("Camera Not Ready")},
	{PTP_RC_CANON_EOS_UnknownCommand,		PTP_VENDOR_CANON, N_("Unknown Command")},
	{PTP_RC_CANON_EOS_OperationRefused,		PTP_VENDOR_CANON, N_("Operation Refused")},
	{PTP_RC_CANON_EOS_LensCoverClosed,		PTP_VENDOR_CANON, N_("Lens Cover Closed")},
	{PTP_RC_CANON_EOS_LowBattery,			PTP_VENDOR_CANON, N_("Low Battery")},
	{PTP_RC_CANON_EOS_ObjectNotReady,		PTP_VENDOR_CANON, N_("Object Not Ready")},
	{PTP_RC_CANON_EOS_CannotMakeObject, 		PTP_VENDOR_CANON, N_("Cannot Make Object")},
	{PTP_RC_CANON_EOS_MemoryStatusNotReady, 	PTP_VENDOR_CANON, N_("Memory Status Not Ready")},

	{PTP_RC_MTP_Undefined,				0,	N_("Undefined")},
	{PTP_RC_MTP_Invalid_ObjectPropCode,		0,	N_("Invalid ObjectPropCode")},
	{PTP_RC_MTP_Invalid_ObjectProp_Format,		0, 	N_("Invalid ObjectProp Format")},
	{PTP_RC_MTP_Invalid_ObjectProp_Value,		0,	N_("Invalid ObjectProp Value")},
	{PTP_RC_MTP_Invalid_ObjectReference,		0,	N_("Invalid ObjectReference")},
	{PTP_RC_MTP_Invalid_Dataset,			0, 	N_("Invalid Dataset")},
	{PTP_RC_MTP_Specification_By_Group_Unsupported,	0,	N_("Specification By Group Unsupported")},
	{PTP_RC_MTP_Specification_By_Depth_Unsupported,	0,	N_("Specification By Depth Unsupported")},
	{PTP_RC_MTP_Object_Too_Large,			0,	N_("Object Too Large")},
	{PTP_RC_MTP_ObjectProp_Not_Supported,		0,	N_("ObjectProp Not Supported")},
	{PTP_RC_MTP_Invalid_Media_Session_ID,		0,	N_("Invalid Media Session ID")},
	{PTP_RC_MTP_Media_Session_Limit_Reached,	0,	N_("Media Session Limit Reached")},
	{PTP_RC_MTP_No_More_Data,			0,	N_("No More Data")},
	{PTP_RC_MTP_Invalid_WFC_Syntax,			0,	N_("Invalid WFC Syntax")},
	{PTP_RC_MTP_WFC_Version_Not_Supported,		0,	N_("WFC Version Not Supported")},

	{PTP_ERROR_NODEVICE,		0, N_("PTP No Device")},
	{PTP_ERROR_TIMEOUT,		0, N_("PTP Timeout")},
	{PTP_ERROR_CANCEL,		0, N_("PTP Cancel Request")},
	{PTP_ERROR_BADPARAM,		0, N_("PTP Invalid Parameter")},
	{PTP_ERROR_RESP_EXPECTED,	0, N_("PTP Response Expected")},
	{PTP_ERROR_DATA_EXPECTED,	0, N_("PTP Data Expected")},
	{PTP_ERROR_IO,			0, N_("PTP I/O Error")},
	{0, 0, NULL}
};

const char *
ptp_strerror(uint16_t ret, uint16_t vendor)
{
	int i;

	for (i=0; ptp_errors[i].txt != NULL; i++)
		if ((ptp_errors[i].rc == ret) && ((ptp_errors[i].vendor == 0) || (ptp_errors[i].vendor == vendor)))
			return ptp_errors[i].txt;
	return NULL;
}

const char*
ptp_data_type_name(PTPParams* params, uint32_t dt)
{
	switch (dt)
	{
	case PTP_DTC_UNDEF:    return "???";
	case PTP_DTC_STR:      return "str";

	case PTP_DTC_INT8:     return "i8 ";
	case PTP_DTC_UINT8:    return "u8 ";
	case PTP_DTC_INT16:    return "i16";
	case PTP_DTC_UINT16:   return "u16";
	case PTP_DTC_INT32:    return "i32";
	case PTP_DTC_UINT32:   return "u32";
	case PTP_DTC_INT64:    return "i64";
	case PTP_DTC_UINT64:   return "u64";
	case PTP_DTC_INT128:   return "i128";
	case PTP_DTC_UINT128:  return "u128";

	case PTP_DTC_AINT8:    return "[i8 ]";
	case PTP_DTC_AUINT8:   return "[u8 ]";
	case PTP_DTC_AINT16:   return "[i16]";
	case PTP_DTC_AUINT16:  return "[u16]";
	case PTP_DTC_AINT32:   return "[i32]";
	case PTP_DTC_AUINT32:  return "[u32]";
	case PTP_DTC_AINT64:   return "[i64]";
	case PTP_DTC_AUINT64:  return "[u64]";
	case PTP_DTC_AINT128:  return "[i128]";
	case PTP_DTC_AUINT128: return "[u128]";

	default:               return "unknown";
	}
}

const char*
ptp_get_property_description(PTPParams* params, uint32_t dpc)
{
	int i;
	/* Device Property descriptions */
	struct {
		uint32_t dpc;
		const char *txt;
	} ptp_device_properties[] = {
		{PTP_DPC_Undefined,		N_("Undefined PTP Property")},
		{PTP_DPC_BatteryLevel,		N_("Battery Level")},
		{PTP_DPC_FunctionalMode,	N_("Functional Mode")},
		{PTP_DPC_ImageSize,		N_("Image Size")},
		{PTP_DPC_CompressionSetting,	N_("Compression Setting")},
		{PTP_DPC_WhiteBalance,		N_("White Balance")},
		{PTP_DPC_RGBGain,		N_("RGB Gain")},
		{PTP_DPC_FNumber,		N_("F-Number")},
		{PTP_DPC_FocalLength,		N_("Focal Length")},
		{PTP_DPC_FocusDistance,		N_("Focus Distance")},
		{PTP_DPC_FocusMode,		N_("Focus Mode")},
		{PTP_DPC_ExposureMeteringMode,	N_("Exposure Metering Mode")},
		{PTP_DPC_FlashMode,		N_("Flash Mode")},
		{PTP_DPC_ExposureTime,		N_("Exposure Time")},
		{PTP_DPC_ExposureProgramMode,	N_("Exposure Program Mode")},
		{PTP_DPC_ExposureIndex,
					N_("Exposure Index (film speed ISO)")},
		{PTP_DPC_ExposureBiasCompensation,
					N_("Exposure Bias Compensation")},
		{PTP_DPC_DateTime,		N_("Date & Time")},
		{PTP_DPC_CaptureDelay,		N_("Pre-Capture Delay")},
		{PTP_DPC_StillCaptureMode,	N_("Still Capture Mode")},
		{PTP_DPC_Contrast,		N_("Contrast")},
		{PTP_DPC_Sharpness,		N_("Sharpness")},
		{PTP_DPC_DigitalZoom,		N_("Digital Zoom")},
		{PTP_DPC_EffectMode,		N_("Effect Mode")},
		{PTP_DPC_BurstNumber,		N_("Burst Number")},
		{PTP_DPC_BurstInterval,		N_("Burst Interval")},
		{PTP_DPC_TimelapseNumber,	N_("Timelapse Number")},
		{PTP_DPC_TimelapseInterval,	N_("Timelapse Interval")},
		{PTP_DPC_FocusMeteringMode,	N_("Focus Metering Mode")},
		{PTP_DPC_UploadURL,		N_("Upload URL")},
		{PTP_DPC_Artist,		N_("Artist")},
		{PTP_DPC_CopyrightInfo,		N_("Copyright Info")},
		{PTP_DPC_SupportedStreams,	N_("Supported Streams")},
		{PTP_DPC_EnabledStreams,	N_("Enabled Streams")},
		{PTP_DPC_VideoFormat,		N_("Video Format")},
		{PTP_DPC_VideoResolution,	N_("Video Resolution")},
		{PTP_DPC_VideoQuality,		N_("Video Quality")},
		{PTP_DPC_VideoFrameRate,	N_("Video Framerate")},
		{PTP_DPC_VideoContrast,		N_("Video Contrast")},
		{PTP_DPC_VideoBrightness,	N_("Video Brightness")},
		{PTP_DPC_AudioFormat,		N_("Audio Format")},
		{PTP_DPC_AudioBitrate,		N_("Audio Bitrate")},
		{PTP_DPC_AudioSamplingRate,	N_("Audio Samplingrate")},
		{PTP_DPC_AudioBitPerSample,	N_("Audio Bits per sample")},
		{PTP_DPC_AudioVolume,		N_("Audio Volume")},
		{0,NULL}
	};
	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_EK[] = {
		{PTP_DPC_EK_ColorTemperature,	N_("Color Temperature")},
		{PTP_DPC_EK_DateTimeStampFormat,
					N_("Date Time Stamp Format")},
		{PTP_DPC_EK_BeepMode,		N_("Beep Mode")},
		{PTP_DPC_EK_VideoOut,		N_("Video Out")},
		{PTP_DPC_EK_PowerSaving,	N_("Power Saving")},
		{PTP_DPC_EK_UI_Language,	N_("UI Language")},
		{0,NULL}
	};

	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_Canon[] = {
		{PTP_DPC_CANON_BeepMode,	N_("Beep Mode")},
		{PTP_DPC_CANON_BatteryKind,	N_("Battery Type")},
		{PTP_DPC_CANON_BatteryStatus,	N_("Battery Mode")},
		{PTP_DPC_CANON_UILockType,	N_("UILockType")},
		{PTP_DPC_CANON_CameraMode,	N_("Camera Mode")},
		{PTP_DPC_CANON_ImageQuality,	N_("Image Quality")},
		{PTP_DPC_CANON_FullViewFileFormat,	N_("Full View File Format")},
		{PTP_DPC_CANON_ImageSize,	N_("Image Size")},
		{PTP_DPC_CANON_SelfTime,	N_("Self Time")},
		{PTP_DPC_CANON_FlashMode,	N_("Flash Mode")},
		{PTP_DPC_CANON_Beep,		N_("Beep")},
		{PTP_DPC_CANON_ShootingMode,	N_("Shooting Mode")},
		{PTP_DPC_CANON_ImageMode,	N_("Image Mode")},
		{PTP_DPC_CANON_DriveMode,	N_("Drive Mode")},
		{PTP_DPC_CANON_EZoom,		N_("Zoom")},
		{PTP_DPC_CANON_MeteringMode,	N_("Metering Mode")},
		{PTP_DPC_CANON_AFDistance,	N_("AF Distance")},
		{PTP_DPC_CANON_FocusingPoint,	N_("Focusing Point")},
		{PTP_DPC_CANON_WhiteBalance,	N_("White Balance")},
		{PTP_DPC_CANON_SlowShutterSetting,	N_("Slow Shutter Setting")},
		{PTP_DPC_CANON_AFMode,		N_("AF Mode")},
		{PTP_DPC_CANON_ImageStabilization,		N_("Image Stabilization")},
		{PTP_DPC_CANON_Contrast,	N_("Contrast")},
		{PTP_DPC_CANON_ColorGain,	N_("Color Gain")},
		{PTP_DPC_CANON_Sharpness,	N_("Sharpness")},
		{PTP_DPC_CANON_Sensitivity,	N_("Sensitivity")},
		{PTP_DPC_CANON_ParameterSet,	N_("Parameter Set")},
		{PTP_DPC_CANON_ISOSpeed,	N_("ISO Speed")},
		{PTP_DPC_CANON_Aperture,	N_("Aperture")},
		{PTP_DPC_CANON_ShutterSpeed,	N_("Shutter Speed")},
		{PTP_DPC_CANON_ExpCompensation,	N_("Exposure Compensation")},
		{PTP_DPC_CANON_FlashCompensation,	N_("Flash Compensation")},
		{PTP_DPC_CANON_AEBExposureCompensation,	N_("AEB Exposure Compensation")},
		{PTP_DPC_CANON_AvOpen,		N_("Av Open")},
		{PTP_DPC_CANON_AvMax,		N_("Av Max")},
		{PTP_DPC_CANON_FocalLength,	N_("Focal Length")},
		{PTP_DPC_CANON_FocalLengthTele,	N_("Focal Length Tele")},
		{PTP_DPC_CANON_FocalLengthWide,	N_("Focal Length Wide")},
		{PTP_DPC_CANON_FocalLengthDenominator,	N_("Focal Length Denominator")},
		{PTP_DPC_CANON_CaptureTransferMode,	N_("Capture Transfer Mode")},
		{PTP_DPC_CANON_Zoom,		N_("Zoom")},
		{PTP_DPC_CANON_NamePrefix,	N_("Name Prefix")},
		{PTP_DPC_CANON_SizeQualityMode,	N_("Size Quality Mode")},
		{PTP_DPC_CANON_SupportedThumbSize,	N_("Supported Thumb Size")},
		{PTP_DPC_CANON_SizeOfOutputDataFromCamera,	N_("Size of Output Data from Camera")},
		{PTP_DPC_CANON_SizeOfInputDataToCamera,		N_("Size of Input Data to Camera")},
		{PTP_DPC_CANON_RemoteAPIVersion,N_("Remote API Version")},
		{PTP_DPC_CANON_FirmwareVersion,	N_("Firmware Version")},
		{PTP_DPC_CANON_CameraModel,	N_("Camera Model")},
		{PTP_DPC_CANON_CameraOwner,	N_("Camera Owner")},
		{PTP_DPC_CANON_UnixTime,	N_("UNIX Time")},
		{PTP_DPC_CANON_CameraBodyID,	N_("Camera Body ID")},
		{PTP_DPC_CANON_CameraOutput,	N_("Camera Output")},
		{PTP_DPC_CANON_DispAv,		N_("Disp Av")},
		{PTP_DPC_CANON_AvOpenApex,	N_("Av Open Apex")},
		{PTP_DPC_CANON_DZoomMagnification,	N_("Digital Zoom Magnification")},
		{PTP_DPC_CANON_MlSpotPos,	N_("Ml Spot Position")},
		{PTP_DPC_CANON_DispAvMax,	N_("Disp Av Max")},
		{PTP_DPC_CANON_AvMaxApex,	N_("Av Max Apex")},
		{PTP_DPC_CANON_EZoomStartPosition,	N_("EZoom Start Position")},
		{PTP_DPC_CANON_FocalLengthOfTele,	N_("Focal Length Tele")},
		{PTP_DPC_CANON_EZoomSizeOfTele,	N_("EZoom Size of Tele")},
		{PTP_DPC_CANON_PhotoEffect,	N_("Photo Effect")},
		{PTP_DPC_CANON_AssistLight,	N_("Assist Light")},
		{PTP_DPC_CANON_FlashQuantityCount,	N_("Flash Quantity Count")},
		{PTP_DPC_CANON_RotationAngle,	N_("Rotation Angle")},
		{PTP_DPC_CANON_RotationScene,	N_("Rotation Scene")},
		{PTP_DPC_CANON_EventEmulateMode,N_("Event Emulate Mode")},
		{PTP_DPC_CANON_DPOFVersion,	N_("DPOF Version")},
		{PTP_DPC_CANON_TypeOfSupportedSlideShow,	N_("Type of Slideshow")},
		{PTP_DPC_CANON_AverageFilesizes,N_("Average Filesizes")},
		{PTP_DPC_CANON_ModelID,		N_("Model ID")},

		{PTP_DPC_CANON_EOS_PowerZoomPosition,"EOS_PowerZoomPosition"},
		{PTP_DPC_CANON_EOS_StrobeSettingSimple,"EOS_StrobeSettingSimple"},
		{PTP_DPC_CANON_EOS_ConnectTrigger,"EOS_ConnectTrigger"},
		{PTP_DPC_CANON_EOS_ChangeCameraMode,"EOS_ChangeCameraMode"},
		{PTP_DPC_CANON_EOS_Aperture,"EOS_Aperture"},
		{PTP_DPC_CANON_EOS_ShutterSpeed,"EOS_ShutterSpeed"},
		{PTP_DPC_CANON_EOS_ISOSpeed,"EOS_ISOSpeed"},
		{PTP_DPC_CANON_EOS_ExpCompensation,"EOS_ExpCompensation"},
		{PTP_DPC_CANON_EOS_AutoExposureMode,"EOS_AutoExposureMode"},
		{PTP_DPC_CANON_EOS_DriveMode,"EOS_DriveMode"},
		{PTP_DPC_CANON_EOS_MeteringMode,"EOS_MeteringMode"},
		{PTP_DPC_CANON_EOS_FocusMode,"EOS_FocusMode"},
		{PTP_DPC_CANON_EOS_WhiteBalance,"EOS_WhiteBalance"},
		{PTP_DPC_CANON_EOS_ColorTemperature,"EOS_ColorTemperature"},
		{PTP_DPC_CANON_EOS_WhiteBalanceAdjustA,"EOS_WhiteBalanceAdjustA"},
		{PTP_DPC_CANON_EOS_WhiteBalanceAdjustB,"EOS_WhiteBalanceAdjustB"},
		{PTP_DPC_CANON_EOS_WhiteBalanceXA,"EOS_WhiteBalanceXA"},
		{PTP_DPC_CANON_EOS_WhiteBalanceXB,"EOS_WhiteBalanceXB"},
		{PTP_DPC_CANON_EOS_ColorSpace,"EOS_ColorSpace"},
		{PTP_DPC_CANON_EOS_PictureStyle,"EOS_PictureStyle"},
		{PTP_DPC_CANON_EOS_BatteryPower,"EOS_BatteryPower"},
		{PTP_DPC_CANON_EOS_BatterySelect,"EOS_BatterySelect"},
		{PTP_DPC_CANON_EOS_CameraTime,"EOS_CameraTime"},
		{PTP_DPC_CANON_EOS_AutoPowerOff,"EOS_AutoPowerOff"},
		{PTP_DPC_CANON_EOS_Owner,"EOS_Owner"},
		{PTP_DPC_CANON_EOS_ModelID,"EOS_ModelID"},
		{PTP_DPC_CANON_EOS_PTPExtensionVersion,"EOS_PTPExtensionVersion"},
		{PTP_DPC_CANON_EOS_DPOFVersion,"EOS_DPOFVersion"},
		{PTP_DPC_CANON_EOS_AvailableShots,"EOS_AvailableShots"},
		{PTP_DPC_CANON_EOS_CaptureDestination,"EOS_CaptureDestination"},
		{PTP_DPC_CANON_EOS_BracketMode,"EOS_BracketMode"},
		{PTP_DPC_CANON_EOS_CurrentStorage,"EOS_CurrentStorage"},
		{PTP_DPC_CANON_EOS_CurrentFolder,"EOS_CurrentFolder"},
		{PTP_DPC_CANON_EOS_ImageFormat,"EOS_ImageFormat"},
		{PTP_DPC_CANON_EOS_ImageFormatCF,"EOS_ImageFormatCF"},
		{PTP_DPC_CANON_EOS_ImageFormatSD,"EOS_ImageFormatSD"},
		{PTP_DPC_CANON_EOS_ImageFormatExtHD,"EOS_ImageFormatExtHD"},
		{PTP_DPC_CANON_EOS_RefocusState,"EOS_RefocusState"},
		{PTP_DPC_CANON_EOS_CameraNickname,"EOS_CameraNickname"},
		{PTP_DPC_CANON_EOS_StroboExpCompositionControl,"EOS_StroboExpCompositionControl"},
		{PTP_DPC_CANON_EOS_ConnectStatus,"EOS_ConnectStatus"},
		{PTP_DPC_CANON_EOS_LensBarrelStatus,"EOS_LensBarrelStatus"},
		{PTP_DPC_CANON_EOS_SilentShutterSetting,"EOS_SilentShutterSetting"},
		{PTP_DPC_CANON_EOS_LV_AF_EyeDetect,"EOS_LV_AF_EyeDetect"},
		{PTP_DPC_CANON_EOS_AutoTransMobile,"EOS_AutoTransMobile"},
		{PTP_DPC_CANON_EOS_URLSupportFormat,"EOS_URLSupportFormat"},
		{PTP_DPC_CANON_EOS_SpecialAcc,"EOS_SpecialAcc"},
		{PTP_DPC_CANON_EOS_CompressionS,"EOS_CompressionS"},
		{PTP_DPC_CANON_EOS_CompressionM1,"EOS_CompressionM1"},
		{PTP_DPC_CANON_EOS_CompressionM2,"EOS_CompressionM2"},
		{PTP_DPC_CANON_EOS_CompressionL,"EOS_CompressionL"},
		{PTP_DPC_CANON_EOS_IntervalShootSetting,"EOS_IntervalShootSetting"},
		{PTP_DPC_CANON_EOS_IntervalShootState,"EOS_IntervalShootState"},
		{PTP_DPC_CANON_EOS_PushMode,"EOS_PushMode"},
		{PTP_DPC_CANON_EOS_LvCFilterKind,"EOS_LvCFilterKind"},
		{PTP_DPC_CANON_EOS_AEModeDial,"EOS_AEModeDial"},
		{PTP_DPC_CANON_EOS_AEModeCustom,"EOS_AEModeCustom"},
		{PTP_DPC_CANON_EOS_MirrorUpSetting,"EOS_MirrorUpSetting"},
		{PTP_DPC_CANON_EOS_HighlightTonePriority,"EOS_HighlightTonePriority"},
		{PTP_DPC_CANON_EOS_AFSelectFocusArea,"EOS_AFSelectFocusArea"},
		{PTP_DPC_CANON_EOS_HDRSetting,"EOS_HDRSetting"},
		{PTP_DPC_CANON_EOS_TimeShootSetting,"EOS_TimeShootSetting"},
		{PTP_DPC_CANON_EOS_NFCApplicationInfo,"EOS_NFCApplicationInfo"},
		{PTP_DPC_CANON_EOS_PCWhiteBalance1,"EOS_PCWhiteBalance1"},
		{PTP_DPC_CANON_EOS_PCWhiteBalance2,"EOS_PCWhiteBalance2"},
		{PTP_DPC_CANON_EOS_PCWhiteBalance3,"EOS_PCWhiteBalance3"},
		{PTP_DPC_CANON_EOS_PCWhiteBalance4,"EOS_PCWhiteBalance4"},
		{PTP_DPC_CANON_EOS_PCWhiteBalance5,"EOS_PCWhiteBalance5"},
		{PTP_DPC_CANON_EOS_MWhiteBalance,"EOS_MWhiteBalance"},
		{PTP_DPC_CANON_EOS_MWhiteBalanceEx,"EOS_MWhiteBalanceEx"},
		{PTP_DPC_CANON_EOS_PowerZoomSpeed,"EOS_PowerZoomSpeed"},
		{PTP_DPC_CANON_EOS_NetworkServerRegion,"EOS_NetworkServerRegion"},
		{PTP_DPC_CANON_EOS_GPSLogCtrl,"EOS_GPSLogCtrl"},
		{PTP_DPC_CANON_EOS_GPSLogListNum,"EOS_GPSLogListNum"},
		{PTP_DPC_CANON_EOS_UnknownPropD14D,"EOS_UnknownPropD14D"},
		{PTP_DPC_CANON_EOS_PictureStyleStandard,"EOS_PictureStyleStandard"},
		{PTP_DPC_CANON_EOS_PictureStylePortrait,"EOS_PictureStylePortrait"},
		{PTP_DPC_CANON_EOS_PictureStyleLandscape,"EOS_PictureStyleLandscape"},
		{PTP_DPC_CANON_EOS_PictureStyleNeutral,"EOS_PictureStyleNeutral"},
		{PTP_DPC_CANON_EOS_PictureStyleFaithful,"EOS_PictureStyleFaithful"},
		{PTP_DPC_CANON_EOS_PictureStyleBlackWhite,"EOS_PictureStyleBlackWhite"},
		{PTP_DPC_CANON_EOS_PictureStyleAuto,"EOS_PictureStyleAuto"},
		{PTP_DPC_CANON_EOS_PictureStyleExStandard,"EOS_PictureStyleExStandard"},
		{PTP_DPC_CANON_EOS_PictureStyleExPortrait,"EOS_PictureStyleExPortrait"},
		{PTP_DPC_CANON_EOS_PictureStyleExLandscape,"EOS_PictureStyleExLandscape"},
		{PTP_DPC_CANON_EOS_PictureStyleExNeutral,"EOS_PictureStyleExNeutral"},
		{PTP_DPC_CANON_EOS_PictureStyleExFaithful,"EOS_PictureStyleExFaithful"},
		{PTP_DPC_CANON_EOS_PictureStyleExBlackWhite,"EOS_PictureStyleExBlackWhite"},
		{PTP_DPC_CANON_EOS_PictureStyleExAuto,"EOS_PictureStyleExAuto"},
		{PTP_DPC_CANON_EOS_PictureStyleExFineDetail,"EOS_PictureStyleExFineDetail"},
		{PTP_DPC_CANON_EOS_PictureStyleUserSet1,"EOS_PictureStyleUserSet1"},
		{PTP_DPC_CANON_EOS_PictureStyleUserSet2,"EOS_PictureStyleUserSet2"},
		{PTP_DPC_CANON_EOS_PictureStyleUserSet3,"EOS_PictureStyleUserSet3"},
		{PTP_DPC_CANON_EOS_PictureStyleExUserSet1,"EOS_PictureStyleExUserSet1"},
		{PTP_DPC_CANON_EOS_PictureStyleExUserSet2,"EOS_PictureStyleExUserSet2"},
		{PTP_DPC_CANON_EOS_PictureStyleExUserSet3,"EOS_PictureStyleExUserSet3"},
		{PTP_DPC_CANON_EOS_MovieAVModeFine,"EOS_MovieAVModeFine"},
		{PTP_DPC_CANON_EOS_ShutterReleaseCounter,"EOS_ShutterReleaseCounter"},
		{PTP_DPC_CANON_EOS_AvailableImageSize,"EOS_AvailableImageSize"},
		{PTP_DPC_CANON_EOS_ErrorHistory,"EOS_ErrorHistory"},
		{PTP_DPC_CANON_EOS_LensExchangeHistory,"EOS_LensExchangeHistory"},
		{PTP_DPC_CANON_EOS_StroboExchangeHistory,"EOS_StroboExchangeHistory"},
		{PTP_DPC_CANON_EOS_PictureStyleParam1,"EOS_PictureStyleParam1"},
		{PTP_DPC_CANON_EOS_PictureStyleParam2,"EOS_PictureStyleParam2"},
		{PTP_DPC_CANON_EOS_PictureStyleParam3,"EOS_PictureStyleParam3"},
		{PTP_DPC_CANON_EOS_MovieRecordVolumeLine,"EOS_MovieRecordVolumeLine"},
		{PTP_DPC_CANON_EOS_NetworkCommunicationMode,"EOS_NetworkCommunicationMode"},
		{PTP_DPC_CANON_EOS_CanonLogGamma,"EOS_CanonLogGamma"},
		{PTP_DPC_CANON_EOS_SmartphoneShowImageConfig,"EOS_SmartphoneShowImageConfig"},
		{PTP_DPC_CANON_EOS_HighISONoiseReduction,"EOS_HighISONoiseReduction"},
		{PTP_DPC_CANON_EOS_MovieServoAF,"EOS_MovieServoAF"},
		{PTP_DPC_CANON_EOS_ContinuousAFValid,"EOS_ContinuousAFValid"},
		{PTP_DPC_CANON_EOS_Attenuator,"EOS_Attenuator"},
		{PTP_DPC_CANON_EOS_UTCTime,"EOS_UTCTime"},
		{PTP_DPC_CANON_EOS_Timezone,"EOS_Timezone"},
		{PTP_DPC_CANON_EOS_Summertime,"EOS_Summertime"},
		{PTP_DPC_CANON_EOS_FlavorLUTParams,"EOS_FlavorLUTParams"},
		{PTP_DPC_CANON_EOS_CustomFunc1,"EOS_CustomFunc1"},
		{PTP_DPC_CANON_EOS_CustomFunc2,"EOS_CustomFunc2"},
		{PTP_DPC_CANON_EOS_CustomFunc3,"EOS_CustomFunc3"},
		{PTP_DPC_CANON_EOS_CustomFunc4,"EOS_CustomFunc4"},
		{PTP_DPC_CANON_EOS_CustomFunc5,"EOS_CustomFunc5"},
		{PTP_DPC_CANON_EOS_CustomFunc6,"EOS_CustomFunc6"},
		{PTP_DPC_CANON_EOS_CustomFunc7,"EOS_CustomFunc7"},
		{PTP_DPC_CANON_EOS_CustomFunc8,"EOS_CustomFunc8"},
		{PTP_DPC_CANON_EOS_CustomFunc9,"EOS_CustomFunc9"},
		{PTP_DPC_CANON_EOS_CustomFunc10,"EOS_CustomFunc10"},
		{PTP_DPC_CANON_EOS_CustomFunc11,"EOS_CustomFunc11"},
		{PTP_DPC_CANON_EOS_CustomFunc12,"EOS_CustomFunc12"},
		{PTP_DPC_CANON_EOS_CustomFunc13,"EOS_CustomFunc13"},
		{PTP_DPC_CANON_EOS_CustomFunc14,"EOS_CustomFunc14"},
		{PTP_DPC_CANON_EOS_CustomFunc15,"EOS_CustomFunc15"},
		{PTP_DPC_CANON_EOS_CustomFunc16,"EOS_CustomFunc16"},
		{PTP_DPC_CANON_EOS_CustomFunc17,"EOS_CustomFunc17"},
		{PTP_DPC_CANON_EOS_CustomFunc18,"EOS_CustomFunc18"},
		{PTP_DPC_CANON_EOS_CustomFunc19,"EOS_CustomFunc19"},
		{PTP_DPC_CANON_EOS_CustomFunc19,"EOS_CustomFunc19"},
		{PTP_DPC_CANON_EOS_InnerDevelop,"EOS_InnerDevelop"},
		{PTP_DPC_CANON_EOS_MultiAspect,"EOS_MultiAspect"},
		{PTP_DPC_CANON_EOS_MovieSoundRecord,"EOS_MovieSoundRecord"},
		{PTP_DPC_CANON_EOS_MovieRecordVolume,"EOS_MovieRecordVolume"},
		{PTP_DPC_CANON_EOS_WindCut,"EOS_WindCut"},
		{PTP_DPC_CANON_EOS_ExtenderType,"EOS_ExtenderType"},
		{PTP_DPC_CANON_EOS_OLCInfoVersion,"EOS_OLCInfoVersion"},
		{PTP_DPC_CANON_EOS_UnknownPropD19A,"EOS_UnknownPropD19A"},
		{PTP_DPC_CANON_EOS_UnknownPropD19C,"EOS_UnknownPropD19C"},
		{PTP_DPC_CANON_EOS_UnknownPropD19D,"EOS_UnknownPropD19D"},
		{PTP_DPC_CANON_EOS_GPSDeviceActive,"EOS_GPSDeviceActive"},
		{PTP_DPC_CANON_EOS_CustomFuncEx,"EOS_CustomFuncEx"},
		{PTP_DPC_CANON_EOS_MyMenu,"EOS_MyMenu"},
		{PTP_DPC_CANON_EOS_MyMenuList,"EOS_MyMenuList"},
		{PTP_DPC_CANON_EOS_WftStatus,"EOS_WftStatus"},
		{PTP_DPC_CANON_EOS_WftInputTransmission,"EOS_WftInputTransmission"},
		{PTP_DPC_CANON_EOS_HDDirectoryStructure,"EOS_HDDirectoryStructure"},
		{PTP_DPC_CANON_EOS_BatteryInfo,"EOS_BatteryInfo"},
		{PTP_DPC_CANON_EOS_AdapterInfo,"EOS_AdapterInfo"},
		{PTP_DPC_CANON_EOS_LensStatus,"EOS_LensStatus"},
		{PTP_DPC_CANON_EOS_QuickReviewTime,"EOS_QuickReviewTime"},
		{PTP_DPC_CANON_EOS_CardExtension,"EOS_CardExtension"},
		{PTP_DPC_CANON_EOS_TempStatus,"EOS_TempStatus"},
		{PTP_DPC_CANON_EOS_ShutterCounter,"EOS_ShutterCounter"},
		{PTP_DPC_CANON_EOS_SpecialOption,"EOS_SpecialOption"},
		{PTP_DPC_CANON_EOS_PhotoStudioMode,"EOS_PhotoStudioMode"},
		{PTP_DPC_CANON_EOS_SerialNumber,"EOS_SerialNumber"},
		{PTP_DPC_CANON_EOS_EVFOutputDevice,"EOS_EVFOutputDevice"},
		{PTP_DPC_CANON_EOS_EVFMode,"EOS_EVFMode"},
		{PTP_DPC_CANON_EOS_DepthOfFieldPreview,"EOS_DepthOfFieldPreview"},
		{PTP_DPC_CANON_EOS_EVFSharpness,"EOS_EVFSharpness"},
		{PTP_DPC_CANON_EOS_EVFWBMode,"EOS_EVFWBMode"},
		{PTP_DPC_CANON_EOS_EVFClickWBCoeffs,"EOS_EVFClickWBCoeffs"},
		{PTP_DPC_CANON_EOS_EVFColorTemp,"EOS_EVFColorTemp"},
		{PTP_DPC_CANON_EOS_ExposureSimMode,"EOS_ExposureSimMode"},
		{PTP_DPC_CANON_EOS_EVFRecordStatus,"EOS_EVFRecordStatus"},
		{PTP_DPC_CANON_EOS_LvAfSystem,"EOS_LvAfSystem"},
		{PTP_DPC_CANON_EOS_MovSize,"EOS_MovSize"},
		{PTP_DPC_CANON_EOS_LvViewTypeSelect,"EOS_LvViewTypeSelect"},
		{PTP_DPC_CANON_EOS_MirrorDownStatus,"EOS_MirrorDownStatus"},
		{PTP_DPC_CANON_EOS_MovieParam,"EOS_MovieParam"},
		{PTP_DPC_CANON_EOS_MirrorLockupState,"EOS_MirrorLockupState"},
		{PTP_DPC_CANON_EOS_FlashChargingState,"EOS_FlashChargingState"},
		{PTP_DPC_CANON_EOS_AloMode,"EOS_AloMode"},
		{PTP_DPC_CANON_EOS_FixedMovie, "EOS_FixedMovie"},
		{PTP_DPC_CANON_EOS_OneShotRawOn,"EOS_OneShotRawOn"},
		{PTP_DPC_CANON_EOS_ErrorForDisplay,"EOS_ErrorForDisplay"},
		{PTP_DPC_CANON_EOS_AEModeMovie,"EOS_AEModeMovie"},
		{PTP_DPC_CANON_EOS_BuiltinStroboMode,"EOS_BuiltinStroboMode"},
		{PTP_DPC_CANON_EOS_StroboDispState,"EOS_StroboDispState"},
		{PTP_DPC_CANON_EOS_StroboETTL2Metering,"EOS_StroboETTL2Metering"},
		{PTP_DPC_CANON_EOS_ContinousAFMode,"EOS_ContinousAFMode"},
		{PTP_DPC_CANON_EOS_MovieParam2,"EOS_MovieParam2"},
		{PTP_DPC_CANON_EOS_StroboExpComposition,"EOS_StroboExpComposition"},
		{PTP_DPC_CANON_EOS_MovieParam3,"EOS_MovieParam3"},
		{PTP_DPC_CANON_EOS_MovieParam4,"EOS_MovieParam4"},
		{PTP_DPC_CANON_EOS_LVMedicalRotate,"EOS_LVMedicalRotate"},
		{PTP_DPC_CANON_EOS_Artist,"EOS_Artist"},
		{PTP_DPC_CANON_EOS_Copyright,"EOS_Copyright"},
		{PTP_DPC_CANON_EOS_BracketValue,"EOS_BracketValue"},
		{PTP_DPC_CANON_EOS_FocusInfoEx,"EOS_FocusInfoEx"},
		{PTP_DPC_CANON_EOS_DepthOfField,"EOS_DepthOfField"},
		{PTP_DPC_CANON_EOS_Brightness,"EOS_Brightness"},
		{PTP_DPC_CANON_EOS_LensAdjustParams,"EOS_LensAdjustParams"},
		{PTP_DPC_CANON_EOS_EFComp,"EOS_EFComp"},
		{PTP_DPC_CANON_EOS_LensName,"EOS_LensName"},
		{PTP_DPC_CANON_EOS_AEB,"EOS_AEB"},
		{PTP_DPC_CANON_EOS_StroboSetting,"EOS_StroboSetting"},
		{PTP_DPC_CANON_EOS_StroboWirelessSetting,"EOS_StroboWirelessSetting"},
		{PTP_DPC_CANON_EOS_StroboFiring,"EOS_StroboFiring"},
		{PTP_DPC_CANON_EOS_LensID,"EOS_LensID"},
		{PTP_DPC_CANON_EOS_LCDBrightness,"EOS_LCDBrightness"},
		{PTP_DPC_CANON_EOS_CADarkBright,"EOS_CADarkBright"},
		{PTP_DPC_CANON_EOS_CAssistPreset,"EOS_CAssistPreset"},
		{PTP_DPC_CANON_EOS_CAssistBrightness,"EOS_CAssistBrightness"},
		{PTP_DPC_CANON_EOS_CAssistContrast,"EOS_CAssistContrast"},
		{PTP_DPC_CANON_EOS_CAssistSaturation,"EOS_CAssistSaturation"},
		{PTP_DPC_CANON_EOS_CAssistColorBA,"EOS_CAssistColorBA"},
		{PTP_DPC_CANON_EOS_CAssistColorMG,"EOS_CAssistColorMG"},
		{PTP_DPC_CANON_EOS_CAssistMonochrome,"EOS_CAssistMonochrome"},
		{PTP_DPC_CANON_EOS_FocusShiftSetting,"EOS_FocusShiftSetting"},
		{PTP_DPC_CANON_EOS_MovieSelfTimer,"EOS_MovieSelfTimer"},
		{PTP_DPC_CANON_EOS_Clarity,"EOS_Clarity"},
		{PTP_DPC_CANON_EOS_2GHDRSetting,"EOS_2GHDRSetting"},
		{PTP_DPC_CANON_EOS_MovieParam5,"EOS_MovieParam5"},
		{PTP_DPC_CANON_EOS_HDRViewAssistModeRec,"EOS_HDRViewAssistModeRec"},
		{PTP_DPC_CANON_EOS_PropFinderAFFrame,"EOS_PropFinderAFFrame"},
		{PTP_DPC_CANON_EOS_VariableMovieRecSetting,"EOS_VariableMovieRecSetting"},
		{PTP_DPC_CANON_EOS_PropAutoRotate,"EOS_PropAutoRotate"},
		{PTP_DPC_CANON_EOS_MFPeakingSetting,"EOS_MFPeakingSetting"},
		{PTP_DPC_CANON_EOS_MovieSpatialOversampling,"EOS_MovieSpatialOversampling"},
		{PTP_DPC_CANON_EOS_MovieCropMode,"EOS_MovieCropMode"},
		{PTP_DPC_CANON_EOS_ShutterType,"EOS_ShutterType"},
		{PTP_DPC_CANON_EOS_WFTBatteryPower,"EOS_WFTBatteryPower"},
		{PTP_DPC_CANON_EOS_BatteryInfoEx,"EOS_BatteryInfoEx"},
		/* add a select few MTP codes here, that EOS cameras have shown to use as well*/
		{PTP_DPC_MTP_DeviceFriendlyName,N_("Friendly Device Name")},     /* D402 */
		{PTP_DPC_MTP_SessionInitiatorInfo,N_("Session Initiator Info")}, /* D406 */
		{PTP_DPC_MTP_PerceivedDeviceType,N_("Perceived Device Type")},   /* D407 */
		{0,NULL}
	};

	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_Nikon[] = {
		{PTP_DPC_NIKON_ShootingBank, 			/* 0xD010 */
		 N_("Shooting Bank")},
		{PTP_DPC_NIKON_ShootingBankNameA,		/* 0xD011 */
		 N_("Shooting Bank Name A")},
		{PTP_DPC_NIKON_ShootingBankNameB,		/* 0xD012 */
		 N_("Shooting Bank Name B")},
		{PTP_DPC_NIKON_ShootingBankNameC,		/* 0xD013 */
		 N_("Shooting Bank Name C")},
		{PTP_DPC_NIKON_ShootingBankNameD,		/* 0xD014 */
		 N_("Shooting Bank Name D")},
		{PTP_DPC_NIKON_ResetBank0,			/* 0xD015 */
		 N_("Reset Bank 0")},
		{PTP_DPC_NIKON_RawCompression,			/* 0xD016 */
		 N_("Raw Compression")},
		{PTP_DPC_NIKON_WhiteBalanceAutoBias,		/* 0xD017 */
		 N_("Auto White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceTungstenBias,	/* 0xD018 */
		 N_("Tungsten White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceFluorescentBias,	/* 0xD019 */
		 N_("Fluorescent White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceDaylightBias,	/* 0xD01a */
		 N_("Daylight White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceFlashBias,		/* 0xD01b */
		 N_("Flash White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceCloudyBias,		/* 0xD01c */
		 N_("Cloudy White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceShadeBias,		/* 0xD01d */
		 N_("Shady White Balance Bias")},
		{PTP_DPC_NIKON_WhiteBalanceColorTemperature,	/* 0xD01e */
		 N_("White Balance Colour Temperature")},
		{PTP_DPC_NIKON_WhiteBalancePresetNo,		/* 0xD01f */
		 N_("White Balance Preset Number")},
		{PTP_DPC_NIKON_WhiteBalancePresetName0,		/* 0xD020 */
		 N_("White Balance Preset Name 0")},
		{PTP_DPC_NIKON_WhiteBalancePresetName1,		/* 0xD021 */
		 N_("White Balance Preset Name 1")},
		{PTP_DPC_NIKON_WhiteBalancePresetName2,		/* 0xD022 */
		 N_("White Balance Preset Name 2")},
		{PTP_DPC_NIKON_WhiteBalancePresetName3,		/* 0xD023 */
		 N_("White Balance Preset Name 3")},
		{PTP_DPC_NIKON_WhiteBalancePresetName4,		/* 0xD024 */
		 N_("White Balance Preset Name 4")},
		{PTP_DPC_NIKON_WhiteBalancePresetVal0,		/* 0xD025 */
		 N_("White Balance Preset Value 0")},
		{PTP_DPC_NIKON_WhiteBalancePresetVal1,		/* 0xD026 */
		 N_("White Balance Preset Value 1")},
		{PTP_DPC_NIKON_WhiteBalancePresetVal2,		/* 0xD027 */
		 N_("White Balance Preset Value 2")},
		{PTP_DPC_NIKON_WhiteBalancePresetVal3,		/* 0xD028 */
		 N_("White Balance Preset Value 3")},
		{PTP_DPC_NIKON_WhiteBalancePresetVal4,		/* 0xD029 */
		 N_("White Balance Preset Value 4")},
		{PTP_DPC_NIKON_ImageSharpening,			/* 0xD02a */
		 N_("Sharpening")},
		{PTP_DPC_NIKON_ToneCompensation,		/* 0xD02b */
		 N_("Tone Compensation")},
		{PTP_DPC_NIKON_ColorModel,			/* 0xD02c */
		 N_("Color Model")},
		{PTP_DPC_NIKON_HueAdjustment,			/* 0xD02d */
		 N_("Hue Adjustment")},
		{PTP_DPC_NIKON_NonCPULensDataFocalLength,	/* 0xD02e */
		 N_("Lens Focal Length (Non CPU)")},
		{PTP_DPC_NIKON_NonCPULensDataMaximumAperture,	/* 0xD02f */
		 N_("Lens Maximum Aperture (Non CPU)")},
		{PTP_DPC_NIKON_ShootingMode,			/* 0xD030 */
		 N_("Shooting Mode")},
		{PTP_DPC_NIKON_JPEG_Compression_Policy,		/* 0xD031 */
		 N_("JPEG Compression Policy")},
		{PTP_DPC_NIKON_ColorSpace,			/* 0xD032 */
		 N_("Color Space")},
		{PTP_DPC_NIKON_AutoDXCrop,			/* 0xD033 */
		 N_("Auto DX Crop")},
		{PTP_DPC_NIKON_FlickerReduction,		/* 0xD034 */
		 N_("Flicker Reduction")},
		{PTP_DPC_NIKON_RemoteMode,			/* 0xD035 */
		 N_("Remote Mode")},
		{PTP_DPC_NIKON_VideoMode,			/* 0xD036 */
		 N_("Video Mode")},
		{PTP_DPC_NIKON_EffectMode,			/* 0xD037 */
		 N_("Effect Mode")},
		{PTP_DPC_NIKON_CSMMenuBankSelect,		/* 0xD040 */
		 "PTP_DPC_NIKON_CSMMenuBankSelect"},
		{PTP_DPC_NIKON_MenuBankNameA,			/* 0xD041 */
		 N_("Menu Bank Name A")},
		{PTP_DPC_NIKON_MenuBankNameB,			/* 0xD042 */
		 N_("Menu Bank Name B")},
		{PTP_DPC_NIKON_MenuBankNameC,			/* 0xD043 */
		 N_("Menu Bank Name C")},
		{PTP_DPC_NIKON_MenuBankNameD,			/* 0xD044 */
		 N_("Menu Bank Name D")},
		{PTP_DPC_NIKON_ResetBank,			/* 0xD045 */
		 N_("Reset Menu Bank")},
		{PTP_DPC_NIKON_A1AFCModePriority,		/* 0xD048 */
		 "PTP_DPC_NIKON_A1AFCModePriority"},
		{PTP_DPC_NIKON_A2AFSModePriority,		/* 0xD049 */
		 "PTP_DPC_NIKON_A2AFSModePriority"},
		{PTP_DPC_NIKON_A3GroupDynamicAF,		/* 0xD04a */
		 "PTP_DPC_NIKON_A3GroupDynamicAF"},
		{PTP_DPC_NIKON_A4AFActivation,			/* 0xD04b */
		 "PTP_DPC_NIKON_A4AFActivation"},
		{PTP_DPC_NIKON_FocusAreaIllumManualFocus,	/* 0xD04c */
		 "PTP_DPC_NIKON_FocusAreaIllumManualFocus"},
		{PTP_DPC_NIKON_FocusAreaIllumContinuous,	/* 0xD04d */
		 "PTP_DPC_NIKON_FocusAreaIllumContinuous"},
		{PTP_DPC_NIKON_FocusAreaIllumWhenSelected,	/* 0xD04e */
		 "PTP_DPC_NIKON_FocusAreaIllumWhenSelected"},
		{PTP_DPC_NIKON_FocusAreaWrap,			/* 0xD04f */
		 N_("Focus Area Wrap")},
		{PTP_DPC_NIKON_VerticalAFON,			/* 0xD050 */
		 N_("Vertical AF On")},
		{PTP_DPC_NIKON_AFLockOn,			/* 0xD051 */
		 N_("AF Lock On")},
		{PTP_DPC_NIKON_FocusAreaZone,			/* 0xD052 */
		 N_("Focus Area Zone")},
		{PTP_DPC_NIKON_EnableCopyright,			/* 0xD053 */
		 N_("Enable Copyright")},
		{PTP_DPC_NIKON_ISOAuto,				/* 0xD054 */
		 N_("Auto ISO")},
		{PTP_DPC_NIKON_EVISOStep,			/* 0xD055 */
		 N_("Exposure ISO Step")},
		{PTP_DPC_NIKON_EVStep,				/* 0xD056 */
		 N_("Exposure Step")},
		{PTP_DPC_NIKON_EVStepExposureComp,		/* 0xD057 */
		 N_("Exposure Compensation (EV)")},
		{PTP_DPC_NIKON_ExposureCompensation,		/* 0xD058 */
		 N_("Exposure Compensation")},
		{PTP_DPC_NIKON_CenterWeightArea,		/* 0xD059 */
		 N_("Centre Weight Area")},
		{PTP_DPC_NIKON_ExposureBaseMatrix,		/* 0xD05A */
		 N_("Exposure Base Matrix")},
		{PTP_DPC_NIKON_ExposureBaseCenter,		/* 0xD05B */
		 N_("Exposure Base Center")},
		{PTP_DPC_NIKON_ExposureBaseSpot,		/* 0xD05C */
		 N_("Exposure Base Spot")},
		{PTP_DPC_NIKON_LiveViewAFArea,			/* 0xD05D */
		 N_("Live View AF Area")},
		{PTP_DPC_NIKON_AELockMode,			/* 0xD05E */
		 N_("Exposure Lock")},
		{PTP_DPC_NIKON_AELAFLMode,			/* 0xD05F */
		 N_("Focus Lock")},
		{PTP_DPC_NIKON_LiveViewAFFocus,			/* 0xD061 */
		 N_("Live View AF Focus")},
		{PTP_DPC_NIKON_MeterOff,			/* 0xD062 */
		 N_("Auto Meter Off Time")},
		{PTP_DPC_NIKON_SelfTimer,			/* 0xD063 */
		 N_("Self Timer Delay")},
		{PTP_DPC_NIKON_MonitorOff,			/* 0xD064 */
		 N_("LCD Off Time")},
		{PTP_DPC_NIKON_ImgConfTime,			/* 0xD065 */
		 N_("Img Conf Time")},
		{PTP_DPC_NIKON_AutoOffTimers,			/* 0xD066 */
		 N_("Auto Off Timers")},
		{PTP_DPC_NIKON_AngleLevel,			/* 0xD067 */
		 N_("Angle Level")},
		{PTP_DPC_NIKON_D1ShootingSpeed,			/* 0xD068 */
		 N_("Shooting Speed")},
		{PTP_DPC_NIKON_D2MaximumShots,			/* 0xD069 */
		 N_("Maximum Shots")},
		{PTP_DPC_NIKON_ExposureDelayMode,		/* 0xD06A */
		 N_("Exposure delay mode")},
		{PTP_DPC_NIKON_LongExposureNoiseReduction,	/* 0xD06B */
		 N_("Long Exposure Noise Reduction")},
		{PTP_DPC_NIKON_FileNumberSequence,		/* 0xD06C */
		 N_("File Number Sequencing")},
		{PTP_DPC_NIKON_ControlPanelFinderRearControl,	/* 0xD06D */
		 "PTP_DPC_NIKON_ControlPanelFinderRearControl"},
		{PTP_DPC_NIKON_ControlPanelFinderViewfinder,	/* 0xD06E */
		 "PTP_DPC_NIKON_ControlPanelFinderViewfinder"},
		{PTP_DPC_NIKON_D7Illumination,			/* 0xD06F */
		 N_("LCD Illumination")},
		{PTP_DPC_NIKON_NrHighISO,			/* 0xD070 */
		 N_("High ISO noise reduction")},
		{PTP_DPC_NIKON_SHSET_CH_GUID_DISP,		/* 0xD071 */
		 N_("On screen tips")},
		{PTP_DPC_NIKON_ArtistName,			/* 0xD072 */
		 N_("Artist Name")},
		{PTP_DPC_NIKON_CopyrightInfo,			/* 0xD073 */
		 N_("Copyright Information")},
		{PTP_DPC_NIKON_FlashSyncSpeed,			/* 0xD074 */
		 N_("Flash Sync. Speed")},
		{PTP_DPC_NIKON_FlashShutterSpeed,		/* 0xD075 */
		 N_("Flash Shutter Speed")},
		{PTP_DPC_NIKON_E3AAFlashMode,			/* 0xD076 */
		 N_("Flash Mode")},
		{PTP_DPC_NIKON_E4ModelingFlash,			/* 0xD077 */
		 N_("Modeling Flash")},
		{PTP_DPC_NIKON_BracketSet,			/* 0xD078 */
		 N_("Bracket Set")},
		{PTP_DPC_NIKON_E6ManualModeBracketing,		/* 0xD079 */
		 N_("Manual Mode Bracketing")},
		{PTP_DPC_NIKON_BracketOrder,			/* 0xD07A */
		 N_("Bracket Order")},
		{PTP_DPC_NIKON_E8AutoBracketSelection,		/* 0xD07B */
		 N_("Auto Bracket Selection")},
		{PTP_DPC_NIKON_BracketingSet, N_("NIKON Auto Bracketing Set")},	/* 0xD07C */
		{PTP_DPC_NIKON_F1CenterButtonShootingMode,	/* 0xD080 */
		 N_("Center Button Shooting Mode")},
		{PTP_DPC_NIKON_CenterButtonPlaybackMode,	/* 0xD081 */
		 N_("Center Button Playback Mode")},
		{PTP_DPC_NIKON_F2Multiselector,			/* 0xD082 */
		 N_("Multiselector")},
		{PTP_DPC_NIKON_F3PhotoInfoPlayback,		/* 0xD083 */
		 N_("Photo Info. Playback")},
		{PTP_DPC_NIKON_F4AssignFuncButton,		/* 0xD084 */
		 N_("Assign Func. Button")},
		{PTP_DPC_NIKON_F5CustomizeCommDials,		/* 0xD085 */
		 N_("Customise Command Dials")},
		{PTP_DPC_NIKON_ReverseCommandDial,		/* 0xD086 */
		 N_("Reverse Command Dial")},
		{PTP_DPC_NIKON_ApertureSetting,			/* 0xD087 */
		 N_("Aperture Setting")},
		{PTP_DPC_NIKON_MenusAndPlayback,		/* 0xD088 */
		 N_("Menus and Playback")},
		{PTP_DPC_NIKON_F6ButtonsAndDials,		/* 0xD089 */
		 N_("Buttons and Dials")},
		{PTP_DPC_NIKON_NoCFCard,			/* 0xD08A */
		 N_("No CF Card Release")},
		{PTP_DPC_NIKON_CenterButtonZoomRatio,		/* 0xD08B */
		 N_("Center Button Zoom Ratio")},
		{PTP_DPC_NIKON_FunctionButton2,			/* 0xD08C */
		 N_("Function Button 2")},
		{PTP_DPC_NIKON_AFAreaPoint,			/* 0xD08D */
		 N_("AF Area Point")},
		{PTP_DPC_NIKON_NormalAFOn,			/* 0xD08E */
		 N_("Normal AF On")},
		{PTP_DPC_NIKON_CleanImageSensor,		/* 0xD08F */
		 N_("Clean Image Sensor")},
		{PTP_DPC_NIKON_ImageCommentString,		/* 0xD090 */
		 N_("Image Comment String")},
		{PTP_DPC_NIKON_ImageCommentEnable,		/* 0xD091 */
		 N_("Image Comment Enable")},
		{PTP_DPC_NIKON_ImageRotation,			/* 0xD092 */
		 N_("Image Rotation")},
		{PTP_DPC_NIKON_ManualSetLensNo,			/* 0xD093 */
		 N_("Manual Set Lens Number")},
		{PTP_DPC_NIKON_MovScreenSize,			/* 0xD0A0 */
		 N_("Movie Screen Size")},
		{PTP_DPC_NIKON_MovVoice,			/* 0xD0A1 */
		 N_("Movie Voice")},
		{PTP_DPC_NIKON_MovMicrophone,			/* 0xD0A2 */
		 N_("Movie Microphone")},
		{PTP_DPC_NIKON_MovFileSlot,			/* 0xD0A3 */
		 N_("Movie Card Slot")},
		{PTP_DPC_NIKON_ManualMovieSetting,		/* 0xD0A6 */
		 N_("Manual Movie Setting")},
		{PTP_DPC_NIKON_MovQuality,			/* 0xD0A7 */
		 N_("Movie Quality")},
		{PTP_DPC_NIKON_MonitorOffDelay,			/* 0xD0B3 */
		 N_("Monitor Off Delay")},
		{PTP_DPC_NIKON_Bracketing,			/* 0xD0C0 */
		 N_("Bracketing Enable")},
		{PTP_DPC_NIKON_AutoExposureBracketStep,		/* 0xD0C1 */
		 N_("Exposure Bracketing Step")},
		{PTP_DPC_NIKON_AutoExposureBracketProgram,	/* 0xD0C2 */
		 N_("Exposure Bracketing Program")},
		{PTP_DPC_NIKON_AutoExposureBracketCount,	/* 0xD0C3 */
		 N_("Auto Exposure Bracket Count")},
		{PTP_DPC_NIKON_WhiteBalanceBracketStep, N_("White Balance Bracket Step")}, /* 0xD0C4 */
		{PTP_DPC_NIKON_WhiteBalanceBracketProgram, N_("White Balance Bracket Program")}, /* 0xD0C5 */
		{PTP_DPC_NIKON_LensID,				/* 0xD0E0 */
		 N_("Lens ID")},
		{PTP_DPC_NIKON_LensSort,			/* 0xD0E1 */
		 N_("Lens Sort")},
		{PTP_DPC_NIKON_LensType,			/* 0xD0E2 */
		 N_("Lens Type")},
		{PTP_DPC_NIKON_FocalLengthMin,			/* 0xD0E3 */
		 N_("Min. Focal Length")},
		{PTP_DPC_NIKON_FocalLengthMax,			/* 0xD0E4 */
		 N_("Max. Focal Length")},
		{PTP_DPC_NIKON_MaxApAtMinFocalLength,		/* 0xD0E5 */
		 N_("Max. Aperture at Min. Focal Length")},
		{PTP_DPC_NIKON_MaxApAtMaxFocalLength,		/* 0xD0E6 */
		 N_("Max. Aperture at Max. Focal Length")},
		{PTP_DPC_NIKON_FinderISODisp,			/* 0xD0F0 */
		 N_("Finder ISO Display")},
		{PTP_DPC_NIKON_AutoOffPhoto,			/* 0xD0F2 */
		 N_("Auto Off Photo")},
		{PTP_DPC_NIKON_AutoOffMenu,			/* 0xD0F3 */
		 N_("Auto Off Menu")},
		{PTP_DPC_NIKON_AutoOffInfo,			/* 0xD0F4 */
		 N_("Auto Off Info")},
		{PTP_DPC_NIKON_SelfTimerShootNum,		/* 0xD0F5 */
		 N_("Self Timer Shot Number")},
		{PTP_DPC_NIKON_VignetteCtrl,			/* 0xD0F7 */
		 N_("Vignette Control")},
		{PTP_DPC_NIKON_AutoDistortionControl,		/* 0xD0F8 */
		 N_("Auto Distortion Control")},
		{PTP_DPC_NIKON_SceneMode,			/* 0xD0F9 */
		 N_("Scene Mode")},
		{PTP_DPC_NIKON_ExposureTime,			/* 0xD100 */
		 N_("Nikon Exposure Time")},
		{PTP_DPC_NIKON_ACPower, N_("AC Power")},	/* 0xD101 */
		{PTP_DPC_NIKON_WarningStatus, N_("Warning Status")},/* 0xD102 */
		{PTP_DPC_NIKON_MaximumShots,			/* 0xD103 */
		 N_("Maximum Shots")},
		{PTP_DPC_NIKON_AFLockStatus, N_("AF Locked")},/* 0xD104 */
		{PTP_DPC_NIKON_AELockStatus, N_("AE Locked")},/* 0xD105 */
		{PTP_DPC_NIKON_FVLockStatus, N_("FV Locked")},/* 0xD106 */
		{PTP_DPC_NIKON_AutofocusLCDTopMode2,		/* 0xD107 */
		 N_("AF LCD Top Mode 2")},
		{PTP_DPC_NIKON_AutofocusArea,			/* 0xD108 */
		 N_("Active AF Sensor")},
		{PTP_DPC_NIKON_FlexibleProgram,			/* 0xD109 */
		 N_("Flexible Program")},
		{PTP_DPC_NIKON_LightMeter,			/* 0xD10A */
		 N_("Exposure Meter")},
		{PTP_DPC_NIKON_RecordingMedia,			/* 0xD10B */
		 N_("Recording Media")},
		{PTP_DPC_NIKON_USBSpeed,			/* 0xD10C */
		 N_("USB Speed")},
		{PTP_DPC_NIKON_CCDNumber,			/* 0xD10D */
		 N_("CCD Serial Number")},
		{PTP_DPC_NIKON_CameraOrientation,		/* 0xD10E */
		 N_("Camera Orientation")},
		{PTP_DPC_NIKON_GroupPtnType,			/* 0xD10F */
		 N_("Group PTN Type")},
		{PTP_DPC_NIKON_FNumberLock,			/* 0xD110 */
		 N_("FNumber Lock")},
		{PTP_DPC_NIKON_ExposureApertureLock,		/* 0xD111 */
		 N_("Exposure Aperture Lock")},
		{PTP_DPC_NIKON_TVLockSetting,			/* 0xD112 */
		 N_("TV Lock Setting")},
		{PTP_DPC_NIKON_AVLockSetting,			/* 0xD113 */
		 N_("AV Lock Setting")},
		{PTP_DPC_NIKON_IllumSetting,			/* 0xD114 */
		 N_("Illum Setting")},
		{PTP_DPC_NIKON_FocusPointBright,		/* 0xD115 */
		 N_("Focus Point Bright")},
		{PTP_DPC_NIKON_ExternalFlashAttached,		/* 0xD120 */
		 N_("External Flash Attached")},
		{PTP_DPC_NIKON_ExternalFlashStatus,		/* 0xD121 */
		 N_("External Flash Status")},
		{PTP_DPC_NIKON_ExternalFlashSort,		/* 0xD122 */
		 N_("External Flash Sort")},
		{PTP_DPC_NIKON_ExternalFlashMode,		/* 0xD123 */
		 N_("External Flash Mode")},
		{PTP_DPC_NIKON_ExternalFlashCompensation,	/* 0xD124 */
		 N_("External Flash Compensation")},
		{PTP_DPC_NIKON_NewExternalFlashMode,		/* 0xD125 */
		 N_("External Flash Mode")},
		{PTP_DPC_NIKON_FlashExposureCompensation,	/* 0xD126 */
		 N_("Flash Exposure Compensation")},
		{PTP_DPC_NIKON_HDRMode,				/* 0xD130 */
		 N_("HDR Mode")},
		{PTP_DPC_NIKON_HDRHighDynamic,			/* 0xD131 */
		 N_("HDR High Dynamic")},
		{PTP_DPC_NIKON_HDRSmoothing,			/* 0xD132 */
		 N_("HDR Smoothing")},
		{PTP_DPC_NIKON_OptimizeImage,			/* 0xD140 */
		 N_("Optimize Image")},
		{PTP_DPC_NIKON_Saturation,			/* 0xD142 */
		 N_("Saturation")},
		{PTP_DPC_NIKON_BW_FillerEffect,			/* 0xD143 */
		 N_("BW Filler Effect")},
		{PTP_DPC_NIKON_BW_Sharpness,			/* 0xD144 */
		 N_("BW Sharpness")},
		{PTP_DPC_NIKON_BW_Contrast,			/* 0xD145 */
		 N_("BW Contrast")},
		{PTP_DPC_NIKON_BW_Setting_Type,			/* 0xD146 */
		 N_("BW Setting Type")},
		{PTP_DPC_NIKON_Slot2SaveMode,			/* 0xD148 */
		 N_("Slot 2 Save Mode")},
		{PTP_DPC_NIKON_RawBitMode,			/* 0xD149 */
		 N_("Raw Bit Mode")},
		{PTP_DPC_NIKON_ActiveDLighting,			/* 0xD14E */
		 N_("Active D-Lighting")},
		{PTP_DPC_NIKON_FlourescentType,			/* 0xD14F */
		 N_("Fluorescent Type")},
		{PTP_DPC_NIKON_TuneColourTemperature,		/* 0xD150 */
		 N_("Tune Colour Temperature")},
		{PTP_DPC_NIKON_TunePreset0,			/* 0xD151 */
		 N_("Tune Preset 0")},
		{PTP_DPC_NIKON_TunePreset1,			/* 0xD152 */
		 N_("Tune Preset 1")},
		{PTP_DPC_NIKON_TunePreset2,			/* 0xD153 */
		 N_("Tune Preset 2")},
		{PTP_DPC_NIKON_TunePreset3,			/* 0xD154 */
		 N_("Tune Preset 3")},
		{PTP_DPC_NIKON_TunePreset4,			/* 0xD155 */
		 N_("Tune Preset 4")},
		{PTP_DPC_NIKON_BeepOff,				/* 0xD160 */
		 N_("AF Beep Mode")},
		{PTP_DPC_NIKON_AutofocusMode,			/* 0xD161 */
		 N_("Autofocus Mode")},
		{PTP_DPC_NIKON_AFAssist,			/* 0xD163 */
		 N_("AF Assist Lamp")},
		{PTP_DPC_NIKON_PADVPMode,			/* 0xD164 */
		 N_("Auto ISO P/A/DVP Setting")},
		{PTP_DPC_NIKON_ImageReview,			/* 0xD165 */
		 N_("Image Review")},
		{PTP_DPC_NIKON_AFAreaIllumination,		/* 0xD166 */
		 N_("AF Area Illumination")},
		{PTP_DPC_NIKON_FlashMode,			/* 0xD167 */
		 N_("Flash Mode")},
		{PTP_DPC_NIKON_FlashCommanderMode,	 	/* 0xD168 */
		 N_("Flash Commander Mode")},
		{PTP_DPC_NIKON_FlashSign,			/* 0xD169 */
		 N_("Flash Sign")},
		{PTP_DPC_NIKON_ISO_Auto,			/* 0xD16A */
		 N_("ISO Auto")},
		{PTP_DPC_NIKON_RemoteTimeout,			/* 0xD16B */
		 N_("Remote Timeout")},
		{PTP_DPC_NIKON_GridDisplay,			/* 0xD16C */
		 N_("Viewfinder Grid Display")},
		{PTP_DPC_NIKON_FlashModeManualPower,		/* 0xD16D */
		 N_("Flash Mode Manual Power")},
		{PTP_DPC_NIKON_FlashModeCommanderPower,		/* 0xD16E */
		 N_("Flash Mode Commander Power")},
		{PTP_DPC_NIKON_AutoFP,				/* 0xD16F */
		 N_("Auto FP")},
		{PTP_DPC_NIKON_CSMMenu,				/* 0xD180 */
		 N_("CSM Menu")},
		{PTP_DPC_NIKON_WarningDisplay,			/* 0xD181 */
		 N_("Warning Display")},
		{PTP_DPC_NIKON_BatteryCellKind,			/* 0xD182 */
		 N_("Battery Cell Kind")},
		{PTP_DPC_NIKON_ISOAutoHiLimit,			/* 0xD183 */
		 N_("ISO Auto High Limit")},
		{PTP_DPC_NIKON_DynamicAFArea,			/* 0xD184 */
		 N_("Dynamic AF Area")},
		{PTP_DPC_NIKON_ContinuousSpeedHigh,		/* 0xD186 */
		 N_("Continuous Speed High")},
		{PTP_DPC_NIKON_InfoDispSetting,			/* 0xD187 */
		 N_("Info Disp Setting")},
		{PTP_DPC_NIKON_PreviewButton,			/* 0xD189 */
		 N_("Preview Button")},
		{PTP_DPC_NIKON_PreviewButton2,			/* 0xD18A */
		 N_("Preview Button 2")},
		{PTP_DPC_NIKON_AEAFLockButton2,			/* 0xD18B */
		 N_("AEAF Lock Button 2")},
		{PTP_DPC_NIKON_IndicatorDisp,			/* 0xD18D */
		 N_("Indicator Display")},
		{PTP_DPC_NIKON_CellKindPriority,		/* 0xD18E */
		 N_("Cell Kind Priority")},
		{PTP_DPC_NIKON_BracketingFramesAndSteps,	/* 0xD190 */
		 N_("Bracketing Frames and Steps")},
		{PTP_DPC_NIKON_LiveViewMode,			/* 0xD1A0 */
		 N_("Live View Mode")},
		{PTP_DPC_NIKON_LiveViewDriveMode,		/* 0xD1A1 */
		 N_("Live View Drive Mode")},
		{PTP_DPC_NIKON_LiveViewStatus,			/* 0xD1A2 */
		 N_("Live View Status")},
		{PTP_DPC_NIKON_LiveViewImageZoomRatio,		/* 0xD1A3 */
		 N_("Live View Image Zoom Ratio")},
		{PTP_DPC_NIKON_LiveViewProhibitCondition,	/* 0xD1A4 */
		 N_("Live View Prohibit Condition")},
		{PTP_DPC_NIKON_ExposureDisplayStatus,		/* 0xD1B0 */
		 N_("Exposure Display Status")},
		{PTP_DPC_NIKON_ExposureIndicateStatus,		/* 0xD1B1 */
		 N_("Exposure Indicate Status")},
		{PTP_DPC_NIKON_InfoDispErrStatus,		/* 0xD1B2 */
		 N_("Info Display Error Status")},
		{PTP_DPC_NIKON_ExposureIndicateLightup,		/* 0xD1B3 */
		 N_("Exposure Indicate Lightup")},
		{PTP_DPC_NIKON_FlashOpen,			/* 0xD1C0 */
		 N_("Flash Open")},
		{PTP_DPC_NIKON_FlashCharged,			/* 0xD1C1 */
		 N_("Flash Charged")},
		{PTP_DPC_NIKON_FlashMRepeatValue,		/* 0xD1D0 */
		 N_("Flash MRepeat Value")},
		{PTP_DPC_NIKON_FlashMRepeatCount,		/* 0xD1D1 */
		 N_("Flash MRepeat Count")},
		{PTP_DPC_NIKON_FlashMRepeatInterval,		/* 0xD1D2 */
		 N_("Flash MRepeat Interval")},
		{PTP_DPC_NIKON_FlashCommandChannel,		/* 0xD1D3 */
		 N_("Flash Command Channel")},
		{PTP_DPC_NIKON_FlashCommandSelfMode,		/* 0xD1D4 */
		 N_("Flash Command Self Mode")},
		{PTP_DPC_NIKON_FlashCommandSelfCompensation,	/* 0xD1D5 */
		 N_("Flash Command Self Compensation")},
		{PTP_DPC_NIKON_FlashCommandSelfValue,		/* 0xD1D6 */
		 N_("Flash Command Self Value")},
		{PTP_DPC_NIKON_FlashCommandAMode,		/* 0xD1D7 */
		 N_("Flash Command A Mode")},
		{PTP_DPC_NIKON_FlashCommandACompensation,	/* 0xD1D8 */
		 N_("Flash Command A Compensation")},
		{PTP_DPC_NIKON_FlashCommandAValue,		/* 0xD1D9 */
		 N_("Flash Command A Value")},
		{PTP_DPC_NIKON_FlashCommandBMode,		/* 0xD1DA */
		 N_("Flash Command B Mode")},
		{PTP_DPC_NIKON_FlashCommandBCompensation,	/* 0xD1DB */
		 N_("Flash Command B Compensation")},
		{PTP_DPC_NIKON_FlashCommandBValue,		/* 0xD1DC */
		 N_("Flash Command B Value")},
		{PTP_DPC_NIKON_ActivePicCtrlItem,		/* 0xD200 */
		 N_("Active Pic Ctrl Item")},
		{PTP_DPC_NIKON_ChangePicCtrlItem,		/* 0xD201 */
		 N_("Change Pic Ctrl Item")},
		/* nikon 1 stuff */
		{PTP_DPC_NIKON_1_ISO,				/* 0xf002 */
		 N_("ISO")},
		{PTP_DPC_NIKON_1_FNumber,			/* 0xf003 */
		 N_("Aperture")},
		{PTP_DPC_NIKON_1_FNumber2,			/* 0xf006 */
		 N_("Aperture")},
		{PTP_DPC_NIKON_1_ShutterSpeed,			/* 0xf004 */
		 N_("Shutterspeed")},
		{PTP_DPC_NIKON_1_ShutterSpeed2,			/* 0xf007 */
		 N_("Shutterspeed")},
		{PTP_DPC_NIKON_1_ImageSize,			/* 0xf00a */
		 N_("Image Size")},
		{PTP_DPC_NIKON_1_LongExposureNoiseReduction,	/* 0xF00D */
		 N_("Long Exposure Noise Reduction")},
		{PTP_DPC_NIKON_1_Language,			/* 0xF018 */
		 N_("Camera Language")},
		{PTP_DPC_NIKON_1_ReleaseWithoutCard,		/* 0xF019 */
		 N_("Release without SD card")},
		{PTP_DPC_NIKON_1_MovQuality,			/* 0xF01C */
		 N_("Movie Quality")},
		{PTP_DPC_NIKON_1_HiISONoiseReduction,		/* 0xF00E */
		 N_("High ISO Noise Reduction")},
		{PTP_DPC_NIKON_1_WhiteBalance,			/* 0xF00C */
		 N_("White Balance")},
		{PTP_DPC_NIKON_1_ImageCompression,		/* 0xF009 */
		 N_("Image Compression")},
		{PTP_DPC_NIKON_1_ActiveDLighting,		/* 0xF00F */
		 N_("Active D-Lighting")},
		{PTP_DPC_NIKON_FaceDetection,"FaceDetection"},
		{PTP_DPC_NIKON_MovRecProhibitCondition,"MovRecProhibitCondition"},
		{PTP_DPC_NIKON_MovRecordMicrophoneLevelValue,"MovRecordMicrophoneLevelValue"},
		{PTP_DPC_NIKON_MovWindNoiseReduction,"MovWindNoiseReduction"},
		{PTP_DPC_NIKON_MovRecordingZone,"MovRecordingZone"},
		{PTP_DPC_NIKON_MovISOAutoControl,"MovISOAutoControl"},
		{PTP_DPC_NIKON_MovISOAutoHighLimit,"MovISOAutoHighLimit"},
		{PTP_DPC_NIKON_MovFileType,"MovFileType"},
		{PTP_DPC_NIKON_LiveViewScreenDisplaySetting,"LiveViewScreenDisplaySetting"},
		{PTP_DPC_NIKON_ExposureIndexEx,"ExposureIndexEx"},
		{PTP_DPC_NIKON_ISOControlSensitivity,"ISOControlSensitivity"},
		{PTP_DPC_NIKON_RawImageSize,"RawImageSize"},
		{PTP_DPC_NIKON_FlickerReductionSetting,"FlickerReductionSetting"},
		{PTP_DPC_NIKON_DiffractionCompensatipn,"DiffractionCompensatipn"},
		{PTP_DPC_NIKON_RemainingExposureTime,"RemainingExposureTime"},
		{PTP_DPC_NIKON_MovieLogOutput,"MovieLogOutput"},
		{PTP_DPC_NIKON_MovieAutoDistortion,"MovieAutoDistortion"},
		{PTP_DPC_NIKON_MovieLogSetting,"MovieLogSetting"},
		{PTP_DPC_NIKON_ADLBracketingPattern,"ADLBracketingPattern"},
		{PTP_DPC_NIKON_ADLBracketingStep,"ADLBracketingStep"},
		{PTP_DPC_NIKON_HDMIOutputDataDepth,"HDMIOutputDataDepth"},
		{PTP_DPC_NIKON_LensTypeML,"LensTypeML"},
		{PTP_DPC_NIKON_UserMode,"UserMode"},
		{PTP_DPC_NIKON_SceneMode2,"SceneMode2"},
		{PTP_DPC_NIKON_SelfTimerInterval,"SelfTimerInterval"},
		{PTP_DPC_NIKON_ExposureCompFlashUsed,"ExposureCompFlashUsed"},
		{PTP_DPC_NIKON_ExternalFlashMultiFlashMode,"ExternalFlashMultiFlashMode"},
		{PTP_DPC_NIKON_ConnectionPath,"ConnectionPath"},
		{PTP_DPC_NIKON_HDRSaveIndividualImages,"HDRSaveIndividualImages"},
		{PTP_DPC_NIKON_VibrationReduction,"VibrationReduction"},
		{PTP_DPC_NIKON_WBAutoType,"WBAutoType"},
		{PTP_DPC_NIKON_WBPresetProtect1,"WBPresetProtect1"},
		{PTP_DPC_NIKON_WBPresetProtect2,"WBPresetProtect2"},
		{PTP_DPC_NIKON_WBPresetProtect3,"WBPresetProtect3"},
		{PTP_DPC_NIKON_ActiveFolder,"ActiveFolder"},
		{PTP_DPC_NIKON_WBPresetProtect4,"WBPresetProtect4"},
		{PTP_DPC_NIKON_WhiteBalanceReset,"WhiteBalanceReset"},
		{PTP_DPC_NIKON_WhiteBalanceNaturalLightAutoBias,"WhiteBalanceNaturalLightAutoBias"},
		{PTP_DPC_NIKON_ISOAutoShutterTime,"ISOAutoShutterTime"},
		{PTP_DPC_NIKON_DateImprintSetting,"DateImprintSetting"},
		{PTP_DPC_NIKON_DateCounterSelect,"DateCounterSelect"},
		{PTP_DPC_NIKON_DateCountData,"DateCountData"},
		{PTP_DPC_NIKON_DateCountDisplaySetting,"DateCountDisplaySetting"},
		{PTP_DPC_NIKON_RangeFinderSetting,"RangeFinderSetting"},
		{PTP_DPC_NIKON_LowLightAF,"LowLightAF"},
		{PTP_DPC_NIKON_ApplyLiveViewSetting,"ApplyLiveViewSetting"},
		{PTP_DPC_NIKON_MovieAfSpeed,"MovieAfSpeed"},
		{PTP_DPC_NIKON_MovieAfSpeedWhenToApply,"MovieAfSpeedWhenToApply"},
		{PTP_DPC_NIKON_MovieAfTrackingSensitivity,"MovieAfTrackingSensitivity"},
		{PTP_DPC_NIKON_MovieWbTuneFlourescent,"MovieWbTuneFlourescent"},
		{PTP_DPC_NIKON_MovieWbTuneSunny,"MovieWbTuneSunny"},
		{PTP_DPC_NIKON_MovieWbTuneCloudy,"MovieWbTuneCloudy"},
		{PTP_DPC_NIKON_MovieWbTuneShade,"MovieWbTuneShade"},
		{PTP_DPC_NIKON_MovieWbColorTemp,"MovieWbColorTemp"},
		{PTP_DPC_NIKON_MovieWbTuneColorTemp,"MovieWbTuneColorTemp"},
		{PTP_DPC_NIKON_MovieWbPresetData0,"MovieWbPresetData0"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment1,"MovieWbPresetDataComment1"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment2,"MovieWbPresetDataComment2"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment3,"MovieWbPresetDataComment3"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment4,"MovieWbPresetDataComment4"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment5,"MovieWbPresetDataComment5"},
		{PTP_DPC_NIKON_MovieWbPresetDataComment6,"MovieWbPresetDataComment6"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue1,"MovieWbPresetDataValue1"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue2,"MovieWbPresetDataValue2"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue3,"MovieWbPresetDataValue3"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue4,"MovieWbPresetDataValue4"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue5,"MovieWbPresetDataValue5"},
		{PTP_DPC_NIKON_MovieWbPresetDataValue6,"MovieWbPresetDataValue6"},
		{PTP_DPC_NIKON_MovieWbTunePreset1,"MovieWbTunePreset1"},
		{PTP_DPC_NIKON_MovieWbTunePreset2,"MovieWbTunePreset2"},
		{PTP_DPC_NIKON_MovieWbTunePreset3,"MovieWbTunePreset3"},
		{PTP_DPC_NIKON_MovieWbTunePreset4,"MovieWbTunePreset4"},
		{PTP_DPC_NIKON_MovieWbTunePreset5,"MovieWbTunePreset5"},
		{PTP_DPC_NIKON_MovieWbTunePreset6,"MovieWbTunePreset6"},
		{PTP_DPC_NIKON_MovieWbPresetProtect1,"MovieWbPresetProtect1"},
		{PTP_DPC_NIKON_MovieWbPresetProtect2,"MovieWbPresetProtect2"},
		{PTP_DPC_NIKON_MovieWbPresetProtect3,"MovieWbPresetProtect3"},
		{PTP_DPC_NIKON_MovieWbPresetProtect4,"MovieWbPresetProtect4"},
		{PTP_DPC_NIKON_MovieWbPresetProtect5,"MovieWbPresetProtect5"},
		{PTP_DPC_NIKON_MovieWbPresetProtect6,"MovieWbPresetProtect6"},
		{PTP_DPC_NIKON_MovieWhiteBalanceReset,"MovieWhiteBalanceReset"},
		{PTP_DPC_NIKON_MovieNrHighISO,"MovieNrHighISO"},
		{PTP_DPC_NIKON_MovieActivePicCtrlItem,"MovieActivePicCtrlItem"},
		{PTP_DPC_NIKON_ExposureBaseCompHighlight,"ExposureBaseCompHighlight"},
		{PTP_DPC_NIKON_MovieWhiteBalance,"MovieWhiteBalance"},
		{PTP_DPC_NIKON_MovieActiveDLighting,"MovieActiveDLighting"},
		{PTP_DPC_NIKON_MovieWbTuneNatural,"MovieWbTuneNatural"},
		{PTP_DPC_NIKON_MovieAttenuator,"MovieAttenuator"},
		{PTP_DPC_NIKON_MovieVignetteControl,"MovieVignetteControl"},
		{PTP_DPC_NIKON_MovieDiffractionCompensation,"MovieDiffractionCompensation"},
		{PTP_DPC_NIKON_UseDeviceStageFlag,"UseDeviceStageFlag"},
		{PTP_DPC_NIKON_ElectronicVR,"ElectronicVR"},
		{PTP_DPC_NIKON_MovieISO,"MovieISO"},
		{PTP_DPC_NIKON_MovieExposureBiasCompensation,"MovieExposureBiasCompensation"},
		{PTP_DPC_NIKON_LiveViewMovieMode,"LiveViewMovieMode"},
		{PTP_DPC_NIKON_MovieExposureMeteringMode,"MovieExposureMeteringMode"},
		{PTP_DPC_NIKON_ContinousShootingCount,"ContinousShootingCount"},
		{PTP_DPC_NIKON_MovieRecFrameCount,"MovieRecFrameCount"},
		{PTP_DPC_NIKON_CameraLiveViewStatus,"CameraLiveViewStatus"},
		{PTP_DPC_NIKON_DetectionPeaking,"DetectionPeaking"},
		{PTP_DPC_NIKON_LiveViewImageStatus,"LiveViewImageStatus"},
		{PTP_DPC_NIKON_LiveViewImageCompression,"LiveViewImageCompression"},
		{PTP_DPC_NIKON_LiveViewZoomArea,"LiveViewZoomArea"},
		{PTP_DPC_NIKON_ExternalRecordingControl,"ExternalRecordingControl"},
		{PTP_DPC_NIKON_HighlightBrightness,"HighlightBrightness"},
		{PTP_DPC_NIKON_SBWirelessMode,"SBWirelessMode"},
		{PTP_DPC_NIKON_SBWirelessMultipleFlashMode,"SBWirelessMultipleFlashMode"},
		{PTP_DPC_NIKON_SBUsableGroup,"SBUsableGroup"},
		{PTP_DPC_NIKON_WirelessCLSEntryMode,"WirelessCLSEntryMode"},
		{PTP_DPC_NIKON_SBPINCode,"SBPINCode"},
		{PTP_DPC_NIKON_RadioMultipleFlashChannel,"RadioMultipleFlashChannel"},
		{PTP_DPC_NIKON_OpticalMultipleFlashChannel,"OpticalMultipleFlashChannel"},
		{PTP_DPC_NIKON_FlashRangeDisplay,"FlashRangeDisplay"},
		{PTP_DPC_NIKON_AllTestFiringDisable,"AllTestFiringDisable"},
		{PTP_DPC_NIKON_SBSettingMemberLock,"SBSettingMemberLock"},
		{PTP_DPC_NIKON_SBIntegrationFlashReady,"SBIntegrationFlashReady"},
		{PTP_DPC_NIKON_ApplicationMode,"ApplicationMode"},
		{PTP_DPC_NIKON_ExposureRemaining,"ExposureRemaining"},
		{PTP_DPC_NIKON_ActiveSlot,"ActiveSlot"},
		{PTP_DPC_NIKON_ISOAutoShutterCorrectionTime,"ISOAutoShutterCorrectionTime"},
		{PTP_DPC_NIKON_MovieAfAreaMode,"MovieAfAreaMode"},
		{PTP_DPC_NIKON_MovieVibrationReduction,"MovieVibrationReduction"},
		{PTP_DPC_NIKON_MovieFocusMode,"MovieFocusMode"},
		{PTP_DPC_NIKON_RecordTimeCodes,"RecordTimeCodes"},
		{PTP_DPC_NIKON_CountUpMethod,"CountUpMethod"},
		{PTP_DPC_NIKON_TimeCodeOrigin,"TimeCodeOrigin"},
		{PTP_DPC_NIKON_DropFrame,"DropFrame"},
		{PTP_DPC_NIKON_ElectronicFrontCurtainShutter,"ElectronicFrontCurtainShutter"},
		{PTP_DPC_NIKON_MovieResetShootingMenu,"MovieResetShootingMenu"},
		{PTP_DPC_NIKON_MovieCaptureAreaCrop,"MovieCaptureAreaCrop"},
		{PTP_DPC_NIKON_MovieWbAutoType,"MovieWbAutoType"},
		{PTP_DPC_NIKON_MovieWbTuneAuto,"MovieWbTuneAuto"},
		{PTP_DPC_NIKON_MovieWbTuneIncandescent,"MovieWbTuneIncandescent"},
		{PTP_DPC_NIKON_MovieWbFlourescentType,"MovieWbFlourescentType"},
		{PTP_DPC_NIKON_FmmManualSetting,"FmmManualSetting"},
		{PTP_DPC_NIKON_F0ManualSetting,"F0ManualSetting"},
		{PTP_DPC_NIKON_CaptureAreaCrop,"CaptureAreaCrop"},
		{PTP_DPC_NIKON_1_Mode,"1_Mode"},
		{PTP_DPC_NIKON_WhiteBalancePresetName5,"WhiteBalancePresetName5"},
		{PTP_DPC_NIKON_WhiteBalancePresetName6,"WhiteBalancePresetName6"},
		{PTP_DPC_NIKON_WhiteBalanceTunePreset5,"WhiteBalanceTunePreset5"},
		{PTP_DPC_NIKON_WhiteBalanceTunePreset6,"WhiteBalanceTunePreset6"},
		{PTP_DPC_NIKON_WhiteBalancePresetProtect5,"WhiteBalancePresetProtect5"},
		{PTP_DPC_NIKON_WhiteBalancePresetProtect6,"WhiteBalancePresetProtect6"},
		{PTP_DPC_NIKON_WhiteBalancePresetValue5,"WhiteBalancePresetValue5"},
		{PTP_DPC_NIKON_WhiteBalancePresetValue6,"WhiteBalancePresetValue6"},
		{PTP_DPC_NIKON_AFStillLockOnMove,"AFStillLockOnMove"},
		{PTP_DPC_NIKON_FocusAreaSelect,"FocusAreaSelect"},
		{PTP_DPC_NIKON_AngleLevelPitching,"AngleLevelPitching"},
		{PTP_DPC_NIKON_AngleLevelYawing,"AngleLevelYawing"},
		{PTP_DPC_NIKON_RetractableLensWarning,"RetractableLensWarning"},
		{PTP_DPC_NIKON_MovieReleaseButton,"MovieReleaseButton"},
		{PTP_DPC_NIKON_FlashISOAutoHighLimit,"FlashISOAutoHighLimit"},
		{PTP_DPC_NIKON_LiveViewSelector,"LiveViewSelector"},
		{PTP_DPC_NIKON_MovieShutterSpeed,"MovieShutterSpeed"},
		{PTP_DPC_NIKON_MovieFNumber,"MovieFNumber"},
		{PTP_DPC_NIKON_MovieCaptureMode,"MovieCaptureMode"},
		{PTP_DPC_NIKON_SlowMotionMovieRecordScreenSize,"SlowMotionMovieRecordScreenSize"},
		{PTP_DPC_NIKON_HighSpeedStillCaptureRate,"HighSpeedStillCaptureRate"},
		{PTP_DPC_NIKON_BestMomentCaptureMode,"BestMomentCaptureMode"},
		{PTP_DPC_NIKON_ActiveSelectionFrameSavedDefault,"ActiveSelectionFrameSavedDefault"},
		{PTP_DPC_NIKON_ActiveSelectionCapture40frameOver,"ActiveSelectionCapture40frameOver"},
		{PTP_DPC_NIKON_ActiveSelectionOnReleaseRecord,"ActiveSelectionOnReleaseRecord"},
		{PTP_DPC_NIKON_ActiveSelectionSelectedPictures,"ActiveSelectionSelectedPictures"},
		{PTP_DPC_NIKON_ExposureRemainingInMovie,"ExposureRemainingInMovie"},
		{PTP_DPC_NIKON_OpticalVR,"OpticalVR"},
		{PTP_DPC_NIKON_SilentPhotography,"SilentPhotography"},
		{PTP_DPC_NIKON_FacePriority,"FacePriority"},
		{PTP_DPC_NIKON_LensTypeNikon1,"LensTypeNikon1"},
		{PTP_DPC_NIKON_ISONoiseReduction,"ISONoiseReduction"},
		{PTP_DPC_NIKON_MirrorUpStatus,"MirrorUpStatus"},
		{PTP_DPC_NIKON_MirrorUpReleaseShootingCount,"MirrorUpReleaseShootingCount"},
		{PTP_DPC_NIKON_AFStillLockOnAcross,"AFStillLockOnAcross"},
		{PTP_DPC_NIKON_ExtendShootingMenu,"ExtendShootingMenu"},
		{PTP_DPC_NIKON_3DTrackingCaptureArea,"3DTrackingCaptureArea"},
		{PTP_DPC_NIKON_MatrixMetering,"MatrixMetering"},
		{PTP_DPC_NIKON_MultiBatteryInfo,"MultiBatteryInfo"},
		{PTP_DPC_NIKON_PrimarySlot,"PrimarySlot"},
		{PTP_DPC_NIKON_LimitedAFAreaMode,"LimitedAFAreaMode"},
		{PTP_DPC_NIKON_AFModeRestrictions,"AFModeRestrictions"},
		{PTP_DPC_NIKON_LiveViewExposurePreview,"LiveViewExposurePreview"},
		{PTP_DPC_NIKON_LiveViewWhiteBalance,"LiveViewWhiteBalance"},
		{PTP_DPC_NIKON_LiveViewImageSize,"LiveViewImageSize"},
		{PTP_DPC_NIKON_LiveViewPhotography,"LiveViewPhotography"},
		{PTP_DPC_NIKON_LiveViewTFTStatus,"LiveViewTFTStatus"},
		{PTP_DPC_NIKON_MovieAutoDxCrop,"MovieAutoDxCrop"},
		{PTP_DPC_NIKON_MovieChangePicCtrlItem,"MovieChangePicCtrlItem"},
		{PTP_DPC_NIKON_MovieLoopLength,"MovieLoopLength"},
		{0,NULL}
	};
	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_MTP[] = {
		{PTP_DPC_MTP_SecureTime,        N_("Secure Time")},		/* D101 */
		{PTP_DPC_MTP_DeviceCertificate, N_("Device Certificate")},	/* D102 */
		{PTP_DPC_MTP_RevocationInfo,    N_("Revocation Info")},		/* D103 */
		{PTP_DPC_MTP_SynchronizationPartner,				/* D401 */
		 N_("Synchronization Partner")},
		{PTP_DPC_MTP_DeviceFriendlyName,				/* D402 */
		 N_("Friendly Device Name")},
		{PTP_DPC_MTP_VolumeLevel,       N_("Volume Level")},		/* D403 */
		{PTP_DPC_MTP_DeviceIcon,        N_("Device Icon")},		/* D405 */
		{PTP_DPC_MTP_SessionInitiatorInfo,	N_("Session Initiator Info")},/* D406 */
		{PTP_DPC_MTP_PerceivedDeviceType,	N_("Perceived Device Type")},/* D407 */
		{PTP_DPC_MTP_PlaybackRate,      N_("Playback Rate")},		/* D410 */
		{PTP_DPC_MTP_PlaybackObject,    N_("Playback Object")},		/* D411 */
		{PTP_DPC_MTP_PlaybackContainerIndex,				/* D412 */
		 N_("Playback Container Index")},
		{PTP_DPC_MTP_PlaybackPosition,  N_("Playback Position")},	/* D413 */
		{PTP_DPC_MTP_PlaysForSureID,    N_("PlaysForSure ID")},		/* D131 (?) */
		{0,NULL}
	};
	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_FUJI[] = {
		{PTP_DPC_FUJI_FilmSimulation, N_("Film Simulation")},	/* 0xD001 */
		{PTP_DPC_FUJI_ColorTemperature, N_("Color Temperature")},	/* 0xD017 */
		{PTP_DPC_FUJI_Quality, N_("Quality")},				/* 0xD018 */
		{PTP_DPC_FUJI_Quality, N_("Release Mode")},			/* 0xD201 */
		{PTP_DPC_FUJI_Quality, N_("Focus Areas")},			/* 0xD206 */
		{PTP_DPC_FUJI_Quality, N_("AE Lock")},				/* 0xD213 */
		{PTP_DPC_FUJI_Quality, N_("Aperture")},				/* 0xD218 */
		{PTP_DPC_FUJI_Quality, N_("Shutter Speed")},			/* 0xD219 */
		{PTP_DPC_FUJI_FocusPoint, N_("Focus Point")},			/* 0xD347 */
		{PTP_DPC_FUJI_RecMode, "RecMode"},
		{PTP_DPC_FUJI_CommandDialMode, "CommandDialMode"},
		{PTP_DPC_FUJI_ExposureIndex, "ExposureIndex"},
		{PTP_DPC_FUJI_MovieISO, "MovieISO"},
		{PTP_DPC_FUJI_LiveViewImageSize, "LiveViewImageSize"},
		{PTP_DPC_FUJI_FocusMeteringMode, "FocusMeteringMode"},
		{PTP_DPC_FUJI_ReleaseMode, "ReleaseMode"},
		{PTP_DPC_FUJI_FocusAreas, "FocusAreas"},
		{PTP_DPC_FUJI_AFStatus, "AFStatus"},
		{PTP_DPC_FUJI_CurrentState, "CurrentState"},
		{PTP_DPC_FUJI_AELock, "AELock"},
		{PTP_DPC_FUJI_Copyright, "Copyright"},
		{PTP_DPC_FUJI_Aperture, "Aperture"},
		{PTP_DPC_FUJI_ShutterSpeed, "ShutterSpeed"},
		{PTP_DPC_FUJI_DeviceError, "DeviceError"},
		{PTP_DPC_FUJI_CaptureRemaining, "CaptureRemaining"},
		{PTP_DPC_FUJI_MovieRemainingTime, "MovieRemainingTime"},
		{PTP_DPC_FUJI_ShutterSpeed2, "ShutterSpeed2"},
		{PTP_DPC_FUJI_ImageAspectRatio, "ImageAspectRatio"},
		{PTP_DPC_FUJI_BatteryLevel, "BatteryLevel"},
		{PTP_DPC_FUJI_InitSequence, "InitSequence"},
		{PTP_DPC_FUJI_AppVersion, "AppVersion"},
		{PTP_DPC_FUJI_DRangeMode,"DRangeMode"},
		{PTP_DPC_FUJI_LiveViewBrightness,"LiveViewBrightness"},
		{PTP_DPC_FUJI_ThroughImageZoom,"ThroughImageZoom"},
		{PTP_DPC_FUJI_NoiseReduction,"NoiseReduction"},
		{PTP_DPC_FUJI_MacroMode,"MacroMode"},
		{PTP_DPC_FUJI_LiveViewStyle,"LiveViewStyle"},
		{PTP_DPC_FUJI_FaceDetectionMode,"FaceDetectionMode"},
		{PTP_DPC_FUJI_RedEyeCorrectionMode,"RedEyeCorrectionMode"},
		{PTP_DPC_FUJI_RawCompression,"RawCompression"},
		{PTP_DPC_FUJI_GrainEffect,"GrainEffect"},
		{PTP_DPC_FUJI_SetEyeAFMode,"SetEyeAFMode"},
		{PTP_DPC_FUJI_FocusPoints,"FocusPoints"},
		{PTP_DPC_FUJI_MFAssistMode,"MFAssistMode"},
		{PTP_DPC_FUJI_InterlockAEAFArea,"InterlockAEAFArea"},
		{PTP_DPC_FUJI_Shadowing,"Shadowing"},
		{PTP_DPC_FUJI_WideDynamicRange,"WideDynamicRange"},
		{PTP_DPC_FUJI_TNumber,"TNumber"},
		{PTP_DPC_FUJI_SerialMode,"SerialMode"},
		{PTP_DPC_FUJI_ExposureDelay,"ExposureDelay"},
		{PTP_DPC_FUJI_PreviewTime,"PreviewTime"},
		{PTP_DPC_FUJI_BlackImageTone,"BlackImageTone"},
		{PTP_DPC_FUJI_Illumination,"Illumination"},
		{PTP_DPC_FUJI_FrameGuideMode,"FrameGuideMode"},
		{PTP_DPC_FUJI_ViewfinderWarning,"ViewfinderWarning"},
		{PTP_DPC_FUJI_AutoImageRotation,"AutoImageRotation"},
		{PTP_DPC_FUJI_DetectImageRotation,"DetectImageRotation"},
		{PTP_DPC_FUJI_ShutterPriorityMode1,"ShutterPriorityMode1"},
		{PTP_DPC_FUJI_ShutterPriorityMode2,"ShutterPriorityMode2"},
		{PTP_DPC_FUJI_AFIlluminator,"AFIlluminator"},
		{PTP_DPC_FUJI_FlashTuneSpeed,"FlashTuneSpeed"},
		{PTP_DPC_FUJI_FlashShutterLimit,"FlashShutterLimit"},
		{PTP_DPC_FUJI_BuiltinFlashMode,"BuiltinFlashMode"},
		{PTP_DPC_FUJI_FlashManualMode,"FlashManualMode"},
		{PTP_DPC_FUJI_ModelingFlash,"ModelingFlash"},
		{PTP_DPC_FUJI_AEAFLockButton,"AEAFLockButton"},
		{PTP_DPC_FUJI_CenterButton,"CenterButton"},
		{PTP_DPC_FUJI_MultiSelectorButton,"MultiSelectorButton"},
		{PTP_DPC_FUJI_FunctionLock,"FunctionLock"},
		{PTP_DPC_FUJI_ButtonsAndDials,"ButtonsAndDials"},
		{PTP_DPC_FUJI_MBD200Batteries,"MBD200Batteries"},
		{PTP_DPC_FUJI_AFOnForMBD200Batteries,"AFOnForMBD200Batteries"},
		{PTP_DPC_FUJI_ShotCount,"ShotCount"},
		{PTP_DPC_FUJI_ShutterExchangeCount,"ShutterExchangeCount"},
		{PTP_DPC_FUJI_WorldClock,"WorldClock"},
		{PTP_DPC_FUJI_Language,"Language"},
		{PTP_DPC_FUJI_FrameNumberSequence,"FrameNumberSequence"},
		{PTP_DPC_FUJI_VideoMode,"VideoMode"},
		{PTP_DPC_FUJI_SetUSBMode,"SetUSBMode"},
		{PTP_DPC_FUJI_CommentWriteSetting,"CommentWriteSetting"},
		{PTP_DPC_FUJI_BCRAppendDelimiter,"BCRAppendDelimiter"},
		{PTP_DPC_FUJI_VideoOutOnOff,"VideoOutOnOff"},
		{PTP_DPC_FUJI_CropMode,"CropMode"},
		{PTP_DPC_FUJI_LensZoomPos,"LensZoomPos"},
		{PTP_DPC_FUJI_FocusPosition,"FocusPosition"},
		{PTP_DPC_FUJI_LiveViewImageQuality,"LiveViewImageQuality"},
		{PTP_DPC_FUJI_LiveViewCondition,"LiveViewCondition"},
		{PTP_DPC_FUJI_LiveViewWhiteBalanceGain,"LiveViewWhiteBalanceGain"},
		{PTP_DPC_FUJI_FocusLength,"FocusLength"},
		{PTP_DPC_FUJI_CropAreaFrameInfo,"CropAreaFrameInfo"},
		{PTP_DPC_FUJI_ResetSetting,"ResetSetting"},
		{PTP_DPC_FUJI_IOPCode,"IOPCode"},
		{PTP_DPC_FUJI_TetherRawConditionCode,"TetherRawConditionCode"},
		{PTP_DPC_FUJI_TetherRawCompatibilityCode,"TetherRawCompatibilityCode"},
		{PTP_DPC_FUJI_LightTune,"LightTune"},
		{PTP_DPC_FUJI_ProgramShift,"ProgramShift"},
		{PTP_DPC_FUJI_PriorityMode,"PriorityMode"},
		{PTP_DPC_FUJI_DeviceName,"DeviceName"},
		{PTP_DPC_FUJI_MediaRecord,"MediaRecord"},
		{PTP_DPC_FUJI_FreeSDRAMImages,"FreeSDRAMImages"},
		{PTP_DPC_FUJI_MediaStatus,"MediaStatus"},
		{PTP_DPC_FUJI_ForceMode,"ForceMode"},
		{PTP_DPC_FUJI_TotalShotCount,"TotalShotCount"},
		{PTP_DPC_FUJI_HighLightTone,"HighLightTone"},
		{PTP_DPC_FUJI_ShadowTone,"ShadowTone"},
		{PTP_DPC_FUJI_LongExposureNR,"LongExposureNR"},
		{PTP_DPC_FUJI_FullTimeManualFocus,"FullTimeManualFocus"},
		{PTP_DPC_FUJI_ISODialHn1,"ISODialHn1"},
		{PTP_DPC_FUJI_ISODialHn2,"ISODialHn2"},
		{PTP_DPC_FUJI_ViewMode1,"ViewMode1"},
		{PTP_DPC_FUJI_ViewMode2,"ViewMode2"},
		{PTP_DPC_FUJI_DispInfoMode,"DispInfoMode"},
		{PTP_DPC_FUJI_LensISSwitch,"LensISSwitch"},
		{PTP_DPC_FUJI_InstantAFMode,"InstantAFMode"},
		{PTP_DPC_FUJI_PreAFMode,"PreAFMode"},
		{PTP_DPC_FUJI_CustomSetting,"CustomSetting"},
		{PTP_DPC_FUJI_LMOMode,"LMOMode"},
		{PTP_DPC_FUJI_LockButtonMode,"LockButtonMode"},
		{PTP_DPC_FUJI_AFLockMode,"AFLockMode"},
		{PTP_DPC_FUJI_MicJackMode,"MicJackMode"},
		{PTP_DPC_FUJI_ISMode,"ISMode"},
		{PTP_DPC_FUJI_DateTimeDispFormat,"DateTimeDispFormat"},
		{PTP_DPC_FUJI_AeAfLockKeyAssign,"AeAfLockKeyAssign"},
		{PTP_DPC_FUJI_CrossKeyAssign,"CrossKeyAssign"},
		{PTP_DPC_FUJI_SilentMode,"SilentMode"},
		{PTP_DPC_FUJI_PBSound,"PBSound"},
		{PTP_DPC_FUJI_EVFDispAutoRotate,"EVFDispAutoRotate"},
		{PTP_DPC_FUJI_ExposurePreview,"ExposurePreview"},
		{PTP_DPC_FUJI_DispBrightness1,"DispBrightness1"},
		{PTP_DPC_FUJI_DispBrightness2,"DispBrightness2"},
		{PTP_DPC_FUJI_DispChroma1,"DispChroma1"},
		{PTP_DPC_FUJI_DispChroma2,"DispChroma2"},
		{PTP_DPC_FUJI_FocusCheckMode,"FocusCheckMode"},
		{PTP_DPC_FUJI_FocusScaleUnit,"FocusScaleUnit"},
		{PTP_DPC_FUJI_SetFunctionButton,"SetFunctionButton"},
		{PTP_DPC_FUJI_SensorCleanTiming,"SensorCleanTiming"},
		{PTP_DPC_FUJI_CustomAutoPowerOff,"CustomAutoPowerOff"},
		{PTP_DPC_FUJI_FileNamePrefix1,"FileNamePrefix1"},
		{PTP_DPC_FUJI_FileNamePrefix2,"FileNamePrefix2"},
		{PTP_DPC_FUJI_CustomDispInfo,"CustomDispInfo"},
		{PTP_DPC_FUJI_CustomPreviewTime,"CustomPreviewTime"},
		{PTP_DPC_FUJI_FocusArea1,"FocusArea1"},
		{PTP_DPC_FUJI_FocusArea2,"FocusArea2"},
		{PTP_DPC_FUJI_FocusArea3,"FocusArea3"},
		{PTP_DPC_FUJI_FrameGuideGridInfo1,"FrameGuideGridInfo1"},
		{PTP_DPC_FUJI_FrameGuideGridInfo2,"FrameGuideGridInfo2"},
		{PTP_DPC_FUJI_FrameGuideGridInfo3,"FrameGuideGridInfo3"},
		{PTP_DPC_FUJI_FrameGuideGridInfo4,"FrameGuideGridInfo4"},
		{PTP_DPC_FUJI_LensZoomPosCaps,"LensZoomPosCaps"},
		{PTP_DPC_FUJI_FocusLimiter,"FocusLimiter"},
		{PTP_DPC_FUJI_FocusArea4,"FocusArea4"},
		{PTP_DPC_FUJI_FilmSimulationTune,"FilmSimulationTune"},
		{PTP_DPC_FUJI_ColorSpace,"ColorSpace"},
		{PTP_DPC_FUJI_WhitebalanceTune1,"WhitebalanceTune1"},
		{PTP_DPC_FUJI_WhitebalanceTune2,"WhitebalanceTune2"},
		{PTP_DPC_FUJI_PhotometryLevel1,"PhotometryLevel1"},
		{PTP_DPC_FUJI_PhotometryLevel2,"PhotometryLevel2"},
		{PTP_DPC_FUJI_PhotometryLevel3,"PhotometryLevel3"},
		{PTP_DPC_FUJI_FlashRepeatingMode1,"FlashRepeatingMode1"},
		{PTP_DPC_FUJI_FlashRepeatingMode2,"FlashRepeatingMode2"},
		{PTP_DPC_FUJI_FlashCommanderMode1,"FlashCommanderMode1"},
		{PTP_DPC_FUJI_FlashCommanderMode2,"FlashCommanderMode2"},
		{PTP_DPC_FUJI_FlashCommanderMode3,"FlashCommanderMode3"},
		{PTP_DPC_FUJI_FlashCommanderMode4,"FlashCommanderMode4"},
		{PTP_DPC_FUJI_FlashCommanderMode5,"FlashCommanderMode5"},
		{PTP_DPC_FUJI_FlashCommanderMode6,"FlashCommanderMode6"},
		{PTP_DPC_FUJI_FlashCommanderMode7,"FlashCommanderMode7"},
		{PTP_DPC_FUJI_BKTSelection,"BKTSelection"},
		{PTP_DPC_FUJI_Password,"Password"},
		{PTP_DPC_FUJI_ChangePassword,"ChangePassword"},
		{PTP_DPC_FUJI_TimeDifference1,"TimeDifference1"},
		{PTP_DPC_FUJI_TimeDifference2,"TimeDifference2"},
		{PTP_DPC_FUJI_Comment,"Comment"},
		{PTP_DPC_FUJI_CommentEx,"CommentEx"},
		{PTP_DPC_FUJI_StandbyMode,"StandbyMode"},
		{PTP_DPC_FUJI_LiveViewExposure,"LiveViewExposure"},
		{PTP_DPC_FUJI_LiveViewTuning,"LiveViewTuning"},
		{PTP_DPC_FUJI_SensitivityFineTune1,"SensitivityFineTune1"},
		{PTP_DPC_FUJI_SensitivityFineTune2,"SensitivityFineTune2"},
		{PTP_DPC_FUJI_LensNameAndSerial,"LensNameAndSerial"},
		{PTP_DPC_FUJI_LensUnknownData,"LensUnknownData"},
		{PTP_DPC_FUJI_LensFNumberList,"LensFNumberList"},
		{PTP_DPC_FUJI_LensFocalLengthList,"LensFocalLengthList"},
		{PTP_DPC_FUJI_ColorMode,"ColorMode"},
		{PTP_DPC_FUJI_Beep,"Beep"},
		{PTP_DPC_FUJI_ISOAutoSetting1,"ISOAutoSetting1"},
		{PTP_DPC_FUJI_ISOAutoSetting2,"ISOAutoSetting2"},
		{PTP_DPC_FUJI_ISOAutoSetting3,"ISOAutoSetting3"},
		{PTP_DPC_FUJI_ExposureStep,"ExposureStep"},
		{PTP_DPC_FUJI_CompensationStep,"CompensationStep"},
		{PTP_DPC_FUJI_ExposureSimpleSet,"ExposureSimpleSet"},
		{PTP_DPC_FUJI_CenterPhotometryRange,"CenterPhotometryRange"},
		{PTP_DPC_FUJI_FlashRepeatingMode3,"FlashRepeatingMode3"},
		{PTP_DPC_FUJI_BKTChange,"BKTChange"},
		{PTP_DPC_FUJI_BKTOrder,"BKTOrder"},
		{PTP_DPC_FUJI_CommandDialSetting1,"CommandDialSetting1"},
		{PTP_DPC_FUJI_CommandDialSetting2,"CommandDialSetting2"},
		{PTP_DPC_FUJI_CommandDialSetting3,"CommandDialSetting3"},
		{PTP_DPC_FUJI_CommandDialSetting4,"CommandDialSetting4"},
		{PTP_DPC_FUJI_NonCPULensData,"NonCPULensData"},
		{PTP_DPC_FUJI_FirmwareVersion,"FirmwareVersion"},
		{PTP_DPC_FUJI_BKTFrame1,"BKTFrame1"},
		{PTP_DPC_FUJI_BKTFrame2,"BKTFrame2"},
		{PTP_DPC_FUJI_BKTStep,"BKTStep"},
		{PTP_DPC_FUJI_MediaCapacity,"MediaCapacity"},
		{PTP_DPC_FUJI_Copyright2,"Copyright2"},
		{PTP_DPC_FUJI_BatteryInfo1,"BatteryInfo1"},
		{PTP_DPC_FUJI_BatteryInfo2,"BatteryInfo2"},
		{PTP_DPC_FUJI_FunctionLockCategory1,"FunctionLockCategory1"},
		{PTP_DPC_FUJI_FunctionLockCategory2,"FunctionLockCategory2"},
		{0,NULL}
	};

	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_SONY[] = {
		{PTP_DPC_WhiteBalance, N_("White Balance")},		/* 0x5005 */
		{PTP_DPC_SONY_ImageStabilization, N_("Image Stabilization")},/* 0xD0D9 */
		{PTP_DPC_SONY_DPCCompensation, N_("DOC Compensation")},	/* 0xD200 */
		{PTP_DPC_SONY_DRangeOptimize, N_("DRangeOptimize")},	/* 0xD201 */
		{PTP_DPC_SONY_ImageSize, N_("Image size")},		/* 0xD203 */
		{PTP_DPC_SONY_ShutterSpeed, N_("Shutter speed")},	/* 0xD20D */
		{PTP_DPC_SONY_ColorTemp, N_("Color temperature")},	/* 0xD20F */
		{PTP_DPC_SONY_CCFilter, ("CC Filter")},			/* 0xD210 */
		{PTP_DPC_SONY_AspectRatio, N_("Aspect Ratio")}, 	/* 0xD211 */
		{PTP_DPC_SONY_FocusFound, N_("Focus Indication")},	/* 0xD213 */
		{PTP_DPC_SONY_Zoom, N_("Zoom")},			/* 0xD214 */
		{PTP_DPC_SONY_ObjectInMemory, N_("Objects in memory")},	/* 0xD215 */
		{PTP_DPC_SONY_ExposeIndex, N_("Expose Index")},		/* 0xD216 */
		{PTP_DPC_SONY_AELockIndication, N_("AELock Indication")},/* 0xD217 */
		{PTP_DPC_SONY_BatteryLevel, N_("Battery Level")},	/* 0xD218 */
		{PTP_DPC_SONY_PictureEffect, N_("Picture Effect")},	/* 0xD21B */
		{PTP_DPC_SONY_ABFilter, N_("AB Filter")},		/* 0xD21C */
		{PTP_DPC_SONY_BatteryLevelIndicator, N_("Battery Level Indicator")},	/* 0xD20E */
		{PTP_DPC_SONY_MovieRecordingState, N_("Movie Recording State")},	/* 0xD21D */
		{PTP_DPC_SONY_ISO, N_("ISO")},				/* 0xD21E */
		{PTP_DPC_SONY_FELockIndication, N_("FELock Indication")},	/* 0xD21F */
		{PTP_DPC_SONY_LiveViewStatus, N_("Live View Status")},	/* 0xD221 */
		{PTP_DPC_SONY_StillImageStoreDestination, N_("Capture Target")},
		{PTP_DPC_SONY_DateTimeSet, N_("Date/Time Set")},
		{PTP_DPC_SONY_ExposureCompensation, N_("Exposure Bias Compensation")},	/* 0xD224 */
		{PTP_DPC_SONY_ISO2, N_("ISO")},				/* 0xD226 */
		{PTP_DPC_SONY_FormatMediaStatus, N_("Format Media Status")},
		{PTP_DPC_SONY_FocusArea, N_("Focus Area")},
		{PTP_DPC_SONY_FocusMagnifierPosition, N_("Focus Magnifier Position")},
		{PTP_DPC_SONY_LiveViewSettingEffect, N_("Live View Setting Effect")},
		{PTP_DPC_SONY_FocusMagnifierStatus, N_("Focus Magnifier Status")},
		{PTP_DPC_SONY_CurrentFocusMagnifierRatio, N_("Current Focus Magnifier Ratio")},
		{PTP_DPC_SONY_ManualFocusAdjustEnableStatus, N_("Manual Focus Adjust Enabled")},
		{PTP_DPC_SONY_PixelShiftShootingMode2, N_("Pixel Shift Shooting Mode")},
		{PTP_DPC_SONY_PixelShiftShootingNumber, N_("Pixel Shift Shooting Number")},
		{PTP_DPC_SONY_PixelShiftShootingInterval, N_("Pixel Shift Shooting Interval")},
		{PTP_DPC_SONY_PixelShiftShootingStatus, N_("Pixel Shift Shooting Status")},
		{PTP_DPC_SONY_PixelShiftShootingProgress, N_("Pixel Shift Shooting Progress")},
		{PTP_DPC_SONY_PictureProfile, N_("Picture Profile")},
		{PTP_DPC_SONY_CreativeStyle, N_("Creative Style")},
		{PTP_DPC_SONY_FileFormatMovie, N_("File Format Movie")},
		{PTP_DPC_SONY_RecordingSettingMovie, N_("Recording Setting Movie")},
		{PTP_DPC_SONY_MediaSLOT1Status, N_("Media SLOT1 Status")},
		{PTP_DPC_SONY_MediaSLOT1RemainingShots, N_("Media SLOT1 Remaining Shots")},
		{PTP_DPC_SONY_MediaSLOT1ShootingTime, N_("Media SLOT1 Shooting Time")},
		{PTP_DPC_SONY_FocalPosition, N_("Focal Position")},
		{PTP_DPC_SONY_AWBLockIndication, N_("AWB Lock Indication")},
		{PTP_DPC_SONY_IntervalRECModel, N_("Interval REC Model")},
		{PTP_DPC_SONY_IntervalRECStatus, N_("Interval REC Status")},
		{PTP_DPC_SONY_DeviceOverheatStatus, N_("Device Overheat Status")},
		{PTP_DPC_SONY_JpegQuality, N_("Jpeg Quality")},
		{PTP_DPC_SONY_CompressionSetting, N_("File Format Still")},
		{PTP_DPC_SONY_FocusMagnifierSetting, N_("Focus Magnifier Setting")},	/* 0xD254 */
		{PTP_DPC_SONY_AFTrackingSensitivity, N_("AF Tracking Sens. Still")},
		{PTP_DPC_SONY_MediaSLOT2Status, N_("Media SLOT2 Status")},
		{PTP_DPC_SONY_MediaSLOT2RemainingShots, N_("Media SLOT2 Remaining Shots")},
		{PTP_DPC_SONY_MediaSLOT2ShootingTime, N_("Media SLOT2 Shooting Time")},
		{PTP_DPC_SONY_PriorityMode, N_("Priority Mode")},
		{PTP_DPC_SONY_ZoomEnableStatus, N_("Zoom Enable Status")},
		{PTP_DPC_SONY_ZoomScale, N_("Zoom Scale")},
		{PTP_DPC_SONY_ZoomBarInformation, N_("Zoom Bar Info")},
		{PTP_DPC_SONY_ZoomSpeedRange, N_("Zoom Speed Range")},
		{PTP_DPC_SONY_ZoomSetting, N_("Zoom Setting")},
		{PTP_DPC_SONY_ZoomTypeStatus, N_("Zoom Type Status")},
		{PTP_DPC_SONY_WirelessFlashSetting, N_("Wireless Flash")},
		{PTP_DPC_SONY_RedEyeReduction, N_("Red Eye Reduction")},
		{PTP_DPC_SONY_RemoteControlRestrictionStatus, N_("Remote Control Restrict")},
		{PTP_DPC_SONY_LiveViewArea, N_("Live View Area")},
		{PTP_DPC_SONY_PcSaveImageSize, N_("PC Save Image Size")},
		{PTP_DPC_SONY_PcSaveImageFormat, N_("PC Save Image Format")},
		{PTP_DPC_SONY_LiveViewImageQuality, N_("Live View Image Quality")},
		{PTP_DPC_SONY_CustomWBCaptureArea, N_("CustomWB Capture Area")},
		{PTP_DPC_SONY_CustomWBCaptureFrameSize, N_("CustomWB Capture Frame Size")},
		{PTP_DPC_SONY_CustomWBCaptureStandbyEnable, N_("CustomWB Capture Standby Enable")},
		{PTP_DPC_SONY_CustomWBCaptureStandbyCancelEnable, N_("CustomWB Capture Standby Cancel Enable")},
		{PTP_DPC_SONY_CustomWBCaptureEnable, N_("CustomWB Capture Enable")},
		{PTP_DPC_SONY_CustomWBExecutionState, N_("CustomWB Execution State")},
		{PTP_DPC_SONY_SettingsSaveEnable, N_("Settings Save Enable")},
		{PTP_DPC_SONY_SettingsReadEnable, N_("Settings Read Enable")},
		{PTP_DPC_SONY_SettingsSaveReadState, N_("Settings Save/Read State")},
		{PTP_DPC_SONY_FTPSettingSaveEnable, N_("FTP Setting Save Enable")},
		{PTP_DPC_SONY_FTPSettingReadEnable, N_("FTP Setting Read Enable")},
		{PTP_DPC_SONY_FTPSettingSaveReadState, N_("FTP Setting Save Read State")},
		{PTP_DPC_SONY_MovieRecButtonHold, N_("MovieRecButtonHold")},
		{PTP_DPC_SONY_RequestOneShooting, N_("RequestOneShooting")},
		{PTP_DPC_SONY_SensorCrop, N_("Sensor Crop")},
		{PTP_DPC_SONY_ShutterHalfRelease, N_("ShutterHalfRelease")},
		{PTP_DPC_SONY_ShutterRelease, N_("ShutterRelease")},
		{PTP_DPC_SONY_AELButton, N_("AELButton")},
		{PTP_DPC_SONY_AFLButton, N_("AFLButton")},
		{PTP_DPC_SONY_ReleaseLock, N_("ReleaseLock")},
		{PTP_DPC_SONY_FELButton, N_("FELButton")},
		{PTP_DPC_SONY_FormatMedia, N_("FormatMedia")},
		{PTP_DPC_SONY_FocusMagnifier, N_("Focus Magnifier")},
		{PTP_DPC_SONY_FocusMagnifierCancel, N_("Focus Magnifier Cancel")},
		{PTP_DPC_SONY_RemoteKeyRight, N_("Remote Key Right")},
		{PTP_DPC_SONY_RemoteKeyLeft, N_("Remote Key Left")},
		{PTP_DPC_SONY_RemoteKeyUp, N_("Remote Key Up")},
		{PTP_DPC_SONY_RemoteKeyDown, N_("Remote Key Down")},
		{PTP_DPC_SONY_ManualFocusAdjust, N_("Manual Focus Adjust")},
		{PTP_DPC_SONY_AFMFHold, N_("AF/MF Hold")},
		{PTP_DPC_SONY_CancelPixelShiftShooting, N_("CancelPixelShiftShooting")},
		{PTP_DPC_SONY_PixelShiftShootingMode, N_("PixelShiftShootingMode")},
		{PTP_DPC_SONY_HFRStandby, N_("HFRStandby")},
		{PTP_DPC_SONY_HFRRecordingCancel, N_("HFRRecordingCancel")},
		{PTP_DPC_SONY_FocusStepNear, N_("FocusStepNear")},
		{PTP_DPC_SONY_FocusStepFar, N_("FocusStepFar")},
		{PTP_DPC_SONY_AWBLButton, N_("AWBLButton")},
		{PTP_DPC_SONY_AFAreaPosition, N_("AF Area Position")},
		{PTP_DPC_SONY_ZoomOperation, N_("Zoom Operation")},
		{PTP_DPC_SONY_CustomWBCaptureStandby, N_("CustomWB Capture Standby")},
		{PTP_DPC_SONY_CustomWBCaptureStandbyCancel, N_("CustomWB Capture Standby Cancel")},
		{PTP_DPC_SONY_CustomWBCapture, N_("CustomWB Capture")},
		{PTP_DPC_SONY_FormatMediaCard, N_("Format Media Card")},
		{PTP_DPC_SONY_RemoteTouch, N_("Remote Touch")},
		{PTP_DPC_SONY_RemoteTouchCancel, N_("Remote Touch Cancel")},
		{PTP_DPC_SONY_SaveZoomAndFocusPosition, N_("Save Zoom and Focus Position")},
		{PTP_DPC_SONY_LoadZoomAndFocusPosition, N_("Load Zoom and Focus Position")},

		{PTP_DPC_SONY_QX_ShutterSpeed, N_("Shutter speed")},
		{PTP_DPC_SONY_QX_ISO, N_("ISO")},
		{PTP_DPC_SONY_QX_Aperture, N_("Aperture")},
		{PTP_DPC_SONY_QX_ExposureCompensation, N_("Exposure Bias Compensation")},
		{PTP_DPC_SONY_QX_DateTime, N_("Date Time")},
		{PTP_DPC_SONY_QX_Zoom_Absolute, "Zoom_Absolute"},
		{PTP_DPC_SONY_QX_Movie_Rec, "Movie_Rec"},
		{PTP_DPC_SONY_QX_Request_For_Update, "Request_For_Update"},
		{PTP_DPC_SONY_QX_Zoom_Wide_For_One_Shot, "Zoom_Wide_For_One_Shot"},
		{PTP_DPC_SONY_QX_Zoom_Tele_For_One_Shot, "Zoom_Tele_For_One_Shot"},
		{PTP_DPC_SONY_QX_S2_Button, "S2_Button"},
		{PTP_DPC_SONY_QX_Media_Format, "Media_Format"},
		{PTP_DPC_SONY_QX_S1_Button, "S1_Button"},
		{PTP_DPC_SONY_QX_AE_Lock, "AE_Lock"},
		{PTP_DPC_SONY_QX_Request_For_Update_For_Lens, "Request_For_Update_For_Lens"},
		{PTP_DPC_SONY_QX_Power_Off, "Power_Off"},
		{PTP_DPC_SONY_QX_RequestOneShooting, "RequestOneShooting"},
		{PTP_DPC_SONY_QX_AF_Lock, "AF_Lock"},
		{PTP_DPC_SONY_QX_Zoom_Tele, "Zoom_Tele"},
		{PTP_DPC_SONY_QX_Zoom_Wide, "Zoom_Wide"},
		{PTP_DPC_SONY_QX_Focus_Magnification, "Focus_Magnification"},
		{PTP_DPC_SONY_QX_Focus_Near_For_One_Shot, "Focus_Near_For_One_Shot"},
		{PTP_DPC_SONY_QX_Focus_Far_For_One_Shot, "Focus_Far_For_One_Shot"},
		{PTP_DPC_SONY_QX_Focus_Near_For_Continuous, "Focus_Near_For_Continuous"},
		{PTP_DPC_SONY_QX_Focus_Far_For_Continuous, "Focus_Far_For_Continuous"},
		{PTP_DPC_SONY_QX_Camera_Setting_Reset, "Camera_Setting_Reset"},
		{PTP_DPC_SONY_QX_Camera_Initialize, "Camera_Initialize"},
		{PTP_DPC_SONY_QX_Capture, "Capture"},
		{PTP_DPC_SONY_QX_AutoFocus, "AutoFocus"},
		{PTP_DPC_SONY_QX_PictureProfileInitialize, "PictureProfileInitialize"},
		{PTP_DPC_SONY_QX_PictureProfile, "PictureProfile"},
		{PTP_DPC_SONY_QX_AFSPrioritySetting, "AFSPrioritySetting"},
		{PTP_DPC_SONY_QX_AFCPrioritySetting, "AFCPrioritySetting"},
		{PTP_DPC_SONY_QX_LensUpdateState, "LensUpdateState"},
		{PTP_DPC_SONY_QX_SilentShooting, "SilentShooting"},
		{PTP_DPC_SONY_QX_HDMIInfoDisplay, "HDMIInfoDisplay"},
		{PTP_DPC_SONY_QX_TCUBDisp, "TCUBDisp"},
		{PTP_DPC_SONY_QX_TCPreset, "TCPreset"},
		{PTP_DPC_SONY_QX_TCMake, "TCMake"},
		{PTP_DPC_SONY_QX_TCRun, "TCRun"},
		{PTP_DPC_SONY_QX_UBPreset, "UBPreset"},
		{PTP_DPC_SONY_QX_TCFormat, "TCFormat"},
		{PTP_DPC_SONY_QX_LongExposureNR, "LongExposureNR"},
		{PTP_DPC_SONY_QX_UBTimeRec, "UBTimeRec"},
		{PTP_DPC_SONY_QX_FocusMagnificationLevel, "FocusMagnificationLevel"},
		{PTP_DPC_SONY_QX_FocusMagnificationPosition, "FocusMagnificationPosition"},
		{PTP_DPC_SONY_QX_LensStatus, "LensStatus"},
		{PTP_DPC_SONY_QX_LiveviewResolution, "LiveviewResolution"},
		{PTP_DPC_SONY_QX_NotifyFocusPosition, "NotifyFocusPosition"},
		{PTP_DPC_SONY_QX_DriveMode, "DriveMode"},
		{PTP_DPC_SONY_QX_AspectRatio, "AspectRatio"},
		{PTP_DPC_SONY_QX_ImageSize, "ImageSize"},
		{PTP_DPC_SONY_QX_WhiteBalance, "WhiteBalance"},
		{PTP_DPC_SONY_QX_CompressionSetting, "CompressionSetting"},
		{PTP_DPC_SONY_QX_CautionError, "CautionError"},
		{PTP_DPC_SONY_QX_StorageInformation, "StorageInformation"},
		{PTP_DPC_SONY_QX_MovieQualitySetting, "MovieQualitySetting"},
		{PTP_DPC_SONY_QX_MovieFormatSetting, "MovieFormatSetting"},
		{PTP_DPC_SONY_QX_ZoomSetAbsolute, "ZoomSetAbsolute"},
		{PTP_DPC_SONY_QX_ZoomInformation, "ZoomInformation"},
		{PTP_DPC_SONY_QX_FocusSpeedForOneShot, "FocusSpeedForOneShot"},
		{PTP_DPC_SONY_QX_FlashCompensation, "FlashCompensation"},
		{PTP_DPC_SONY_QX_ShootingFileInformation, "ShootingFileInformation"},
		{PTP_DPC_SONY_QX_MediaFormatState, "MediaFormatState"},
		{PTP_DPC_SONY_QX_ZoomMode, "ZoomMode"},
		{PTP_DPC_SONY_QX_FlashMode, "FlashMode"},
		{PTP_DPC_SONY_QX_FocusMode, "FocusMode"},
		{PTP_DPC_SONY_QX_ExposureMode, "ExposureMode"},
		{PTP_DPC_SONY_QX_MovieRecordingState, "MovieRecordingState"},
		{PTP_DPC_SONY_QX_SelectSaveMedia, "SelectSaveMedia"},
		{PTP_DPC_SONY_QX_StillSteady, "StillSteady"},
		{PTP_DPC_SONY_QX_MovieSteady, "MovieSteady"},
		{PTP_DPC_SONY_QX_Housing, "Housing"},
		{PTP_DPC_SONY_QX_K4OutputSetting, "K4OutputSetting"},
		{PTP_DPC_SONY_QX_HDMIRECControl, "HDMIRECControl"},
		{PTP_DPC_SONY_QX_TimeCodeOutputToHDMI, "TimeCodeOutputToHDMI"},
		{PTP_DPC_SONY_QX_HDMIResolution, "HDMIResolution"},
		{PTP_DPC_SONY_QX_NTSC_PAL_Selector, "NTSC_PAL_Selector"},
		{PTP_DPC_SONY_QX_HDMIOutput, "HDMIOutput"},
		{PTP_DPC_SONY_QX_ISOAutoMinimum, "ISOAutoMinimum"},
		{PTP_DPC_SONY_QX_ISOAutoMaximum, "ISOAutoMaximum"},
		{PTP_DPC_SONY_QX_APSCSuper35mm, "APSCSuper35mm"},
		{PTP_DPC_SONY_QX_LiveviewStatus, "LiveviewStatus"},
		{PTP_DPC_SONY_QX_WhiteBalanceInitialize, "WhiteBalanceInitialize"},
		{PTP_DPC_SONY_QX_OperatingMode, "OperatingMode"},
		{PTP_DPC_SONY_QX_BiaxialFineTuningABDirection, "BiaxialFineTuningABDirection"},
		{PTP_DPC_SONY_QX_HighISONr, "HighISONr"},
		{PTP_DPC_SONY_QX_AELockIndication, "AELockIndication"},
		{PTP_DPC_SONY_QX_ElectronicFrontCurtainShutter, "ElectronicFrontCurtainShutter"},
		{PTP_DPC_SONY_QX_FocusIndication, "FocusIndication"},
		{PTP_DPC_SONY_QX_BiaxialFineTuningGMDirection, "BiaxialFineTuningGMDirection"},
		{PTP_DPC_SONY_QX_ColorTemperature, "ColorTemperature"},
		{PTP_DPC_SONY_QX_BatteryLevelIndication, "BatteryLevelIndication"},
		{PTP_DPC_SONY_QX_AutoSlowShutter, "AutoSlowShutter"},
		{PTP_DPC_SONY_QX_DynamicRangeOptimizer, "DynamicRangeOptimizer"},
		{0,NULL}
	};

	struct {
		uint16_t dpc;
		const char *txt;
	} ptp_device_properties_PARROT[] = {
		{PTP_DPC_PARROT_PhotoSensorEnableMask,		"PhotoSensorEnableMask"}, /* 0xD201 */
		{PTP_DPC_PARROT_PhotoSensorsKeepOn,		"PhotoSensorsKeepOn"}, /* 0xD202 */
		{PTP_DPC_PARROT_MultispectralImageSize,		"MultispectralImageSize"}, /* 0xD203 */
		{PTP_DPC_PARROT_MainBitDepth,			"MainBitDepth"}, /* 0xD204 */
		{PTP_DPC_PARROT_MultispectralBitDepth,		"MultispectralBitDepth"}, /* 0xD205 */
		{PTP_DPC_PARROT_HeatingEnable,			"HeatingEnable"}, /* 0xD206 */
		{PTP_DPC_PARROT_WifiStatus,			"WifiStatus"}, /* 0xD207 */
		{PTP_DPC_PARROT_WifiSSID,			"WifiSSID"}, /* 0xD208 */
		{PTP_DPC_PARROT_WifiEncryptionType,		"WifiEncryptionType"}, /* 0xD209 */
		{PTP_DPC_PARROT_WifiPassphrase,			"WifiPassphrase"}, /* 0xD20A */
		{PTP_DPC_PARROT_WifiChannel,			"WifiChannel"}, /* 0xD20B */
		{PTP_DPC_PARROT_Localization,			"Localization"}, /* 0xD20C */
		{PTP_DPC_PARROT_WifiMode,			"WifiMode"}, /* 0xD20D */
		{PTP_DPC_PARROT_AntiFlickeringFrequency,	"AntiFlickeringFrequency"}, /* 0xD210 */
		{PTP_DPC_PARROT_DisplayOverlayMask,		"DisplayOverlayMask"}, /* 0xD211 */
		{PTP_DPC_PARROT_GPSInterval,			"GPSInterval"}, /* 0xD212 */
		{PTP_DPC_PARROT_MultisensorsExposureMeteringMode,"MultisensorsExposureMeteringMode"}, /* 0xD213 */
		{PTP_DPC_PARROT_MultisensorsExposureTime,	"MultisensorsExposureTime"}, /* 0xD214 */
		{PTP_DPC_PARROT_MultisensorsExposureProgramMode,"MultisensorsExposureProgramMode"}, /* 0xD215 */
		{PTP_DPC_PARROT_MultisensorsExposureIndex,	"MultisensorsExposureIndex"}, /* 0xD216 */
		{PTP_DPC_PARROT_MultisensorsIrradianceGain,	"MultisensorsIrradianceGain"}, /* 0xD217 */
		{PTP_DPC_PARROT_MultisensorsIrradianceIntegrationTime,"MultisensorsIrradianceIntegrationTime"}, /* 0xD218 */
		{PTP_DPC_PARROT_OverlapRate,			"OverlapRate"}, /* 0xD219 */
		{0,NULL}
	};


	for (i=0; ptp_device_properties[i].txt!=NULL; i++)
		if (ptp_device_properties[i].dpc==dpc)
			return (ptp_device_properties[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_MICROSOFT
	    || params->deviceinfo.VendorExtensionID==PTP_VENDOR_MTP
	    || params->deviceinfo.VendorExtensionID==PTP_VENDOR_PANASONIC)
		for (i=0; ptp_device_properties_MTP[i].txt!=NULL; i++)
			if (ptp_device_properties_MTP[i].dpc==dpc)
				return (ptp_device_properties_MTP[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_EASTMAN_KODAK)
		for (i=0; ptp_device_properties_EK[i].txt!=NULL; i++)
			if (ptp_device_properties_EK[i].dpc==dpc)
				return (ptp_device_properties_EK[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_CANON)
		for (i=0; ptp_device_properties_Canon[i].txt!=NULL; i++)
			if (ptp_device_properties_Canon[i].dpc==dpc)
				return (ptp_device_properties_Canon[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_NIKON)
		for (i=0; ptp_device_properties_Nikon[i].txt!=NULL; i++)
			if (ptp_device_properties_Nikon[i].dpc==dpc)
				return (ptp_device_properties_Nikon[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_FUJI)
		for (i=0; ptp_device_properties_FUJI[i].txt!=NULL; i++)
			if (ptp_device_properties_FUJI[i].dpc==dpc)
				return (ptp_device_properties_FUJI[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_SONY)
		for (i=0; ptp_device_properties_SONY[i].txt!=NULL; i++)
			if (ptp_device_properties_SONY[i].dpc==dpc)
				return (ptp_device_properties_SONY[i].txt);

	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_PARROT)
		for (i=0; ptp_device_properties_PARROT[i].txt!=NULL; i++)
			if (ptp_device_properties_PARROT[i].dpc==dpc)
				return (ptp_device_properties_PARROT[i].txt);

	return NULL;
}

static int64_t
_value_to_num(PTPPropValue *data, uint16_t dt) {
	if (dt == PTP_DTC_STR) {
		if (!data->str)
			return 0;
		return atol(data->str);
	}
	if (dt & PTP_DTC_ARRAY_MASK) {
		return 0;
	} else {
		switch (dt) {
		case PTP_DTC_UNDEF:  return 0;
		case PTP_DTC_INT8:   return data->i8;
		case PTP_DTC_UINT8:  return data->u8;
		case PTP_DTC_INT16:  return data->i16;
		case PTP_DTC_UINT16: return data->u16;
		case PTP_DTC_INT32:  return data->i32;
		case PTP_DTC_UINT32: return data->u32;
	/*
		PTP_DTC_INT64
		PTP_DTC_UINT64
		PTP_DTC_INT128
		PTP_DTC_UINT128
	*/
		default:             return 0;
		}
	}

	return 0;
}

#define PTP_VAL_BOOL(dpc) {dpc, 0, N_("Off")}, {dpc, 1, N_("On")}
#define PTP_VENDOR_VAL_BOOL(dpc,vendor) {dpc, vendor, 0, N_("Off")}, {dpc, vendor, 1, N_("On")}
#define PTP_VENDOR_VAL_RBOOL(dpc,vendor) {dpc, vendor, 0, N_("On")}, {dpc, vendor, 1, N_("Off")}
#define PTP_VENDOR_VAL_YN(dpc,vendor) {dpc, vendor, 0, N_("No")}, {dpc, vendor, 1, N_("Yes")}

int
ptp_render_property_value(PTPParams* params, uint16_t dpc,
			  PTPDevicePropDesc *dpd, unsigned int length, char *out)
{
	unsigned int i;
	int64_t	kval;

	struct {
		uint16_t dpc;
		uint16_t vendor;
		double coef;
		double bias;
		const char *format;
	} ptp_value_trans[] = {
		{PTP_DPC_BatteryLevel, 0, 1.0, 0.0, "%.0f%%"},		/* 5001 */
		{PTP_DPC_FNumber, 0, 0.01, 0.0, "f/%.2g"},		/* 5007 */
		{PTP_DPC_FocalLength, 0, 0.01, 0.0, "%.0f mm"},		/* 5008 */
		{PTP_DPC_FocusDistance, 0, 0.01, 0.0, "%.0f mm"},	/* 5009 */
		{PTP_DPC_ExposureTime, 0, 0.00001, 0.0, "%.2g sec"},	/* 500D */
		{PTP_DPC_ExposureIndex, 0, 1.0, 0.0, "ISO %.0f"},	/* 500F */
		{PTP_DPC_ExposureBiasCompensation, 0, 0.001, 0.0, N_("%.1f stops")},/* 5010 */
		{PTP_DPC_CaptureDelay, 0, 0.001, 0.0, "%.1fs"},		/* 5012 */
		{PTP_DPC_DigitalZoom, 0, 0.1, 0.0, "%.1f"},		/* 5016 */
		{PTP_DPC_BurstInterval, 0, 0.001, 0.0, "%.1fs"},	/* 5019 */
		{PTP_DPC_TimelapseInterval, 0, 0.0001, 0.0, "%.1fs"},	/* 5019 */
		{PTP_DPC_SupportedStreams, 0, 0, 0, "0x%x"},		/* 5020 */
		{PTP_DPC_EnabledStreams, 0, 0, 0, "0x%x"},		/* 5021 */
		{PTP_DPC_VideoFrameRate, 0, 0, 0.0000001, "%.1f s/f"},	/* 5021 */

		/* Nikon device properties */
		{PTP_DPC_NIKON_LightMeter, PTP_VENDOR_NIKON, 0.08333, 0.0, N_("%.1f stops")},/* D10A */
		{PTP_DPC_NIKON_FlashExposureCompensation, PTP_VENDOR_NIKON, 0.16666, 0.0, N_("%.1f stops")}, /* D126 */
		{PTP_DPC_NIKON_CenterWeightArea, PTP_VENDOR_NIKON, 2.0, 6.0, N_("%.0f mm")},/* D059 */
		{PTP_DPC_NIKON_FocalLengthMin, PTP_VENDOR_NIKON, 0.01, 0.0, "%.0f mm"}, /* D0E3 */
		{PTP_DPC_NIKON_FocalLengthMax, PTP_VENDOR_NIKON, 0.01, 0.0, "%.0f mm"}, /* D0E4 */
		{PTP_DPC_NIKON_MaxApAtMinFocalLength, PTP_VENDOR_NIKON, 0.01, 0.0, "f/%.2g"}, /* D0E5 */
		{PTP_DPC_NIKON_MaxApAtMaxFocalLength, PTP_VENDOR_NIKON, 0.01, 0.0, "f/%.2g"}, /* D0E6 */
		{PTP_DPC_NIKON_ExternalFlashCompensation, PTP_VENDOR_NIKON, 1.0/6.0, 0.0,"%.0f"}, /* D124 */
		{PTP_DPC_NIKON_ExposureIndicateStatus, PTP_VENDOR_NIKON, 0.08333, 0.0, N_("%.1f stops")},/* D1B1 - FIXME: check if correct. */
		{PTP_DPC_NIKON_AngleLevel, PTP_VENDOR_NIKON, 1.0/65536, 0.0, "%.1f'"},/* 0xD067 */
		{0, 0, 0.0, 0.0, NULL}
	};

	struct {
		uint16_t dpc;
		uint16_t vendor;
		int64_t key;
		char *value;
	} ptp_value_list[] = {
		{PTP_DPC_CompressionSetting, 0, 0, N_("JPEG Basic")},	/* 5004 */
		{PTP_DPC_CompressionSetting, 0, 1, N_("JPEG Norm")},
		{PTP_DPC_CompressionSetting, 0, 2, N_("JPEG Fine")},
		{PTP_DPC_CompressionSetting, 0, 4, N_("RAW")},
		{PTP_DPC_CompressionSetting, 0, 5, N_("RAW + JPEG Basic")},
		{PTP_DPC_WhiteBalance, 0, 1, N_("Manual")},
		{PTP_DPC_WhiteBalance, 0, 2, N_("Automatic")},		/* 5005 */
		{PTP_DPC_WhiteBalance, 0, 3, N_("One-push Automatic")},
		{PTP_DPC_WhiteBalance, 0, 4, N_("Daylight")},
		{PTP_DPC_WhiteBalance, 0, 5, N_("Fluorescent")},
		{PTP_DPC_WhiteBalance, 0, 6, N_("Incandescent")},
		{PTP_DPC_WhiteBalance, 0, 7, N_("Flash")},
		{PTP_DPC_WhiteBalance, PTP_VENDOR_NIKON, 32784, N_("Cloudy")},
		{PTP_DPC_WhiteBalance, PTP_VENDOR_NIKON, 32785, N_("Shade")},
		{PTP_DPC_WhiteBalance, PTP_VENDOR_NIKON, 32786, N_("Color Temperature")},
		{PTP_DPC_WhiteBalance, PTP_VENDOR_NIKON, 32787, N_("Preset")},
		{PTP_DPC_FocusMode, 0, 1, N_("Manual Focus")},		/* 500A */
		{PTP_DPC_FocusMode, 0, 2, N_("Automatic")},
		{PTP_DPC_FocusMode, 0, 3, N_("Automatic Macro (close-up)")},
		{PTP_DPC_FocusMode, PTP_VENDOR_NIKON, 32784, "AF-S"},
		{PTP_DPC_FocusMode, PTP_VENDOR_NIKON, 32785, "AF-C"},
		{PTP_DPC_FocusMode, PTP_VENDOR_NIKON, 32786, "AF-A"},
		{PTP_DPC_ExposureMeteringMode, 0, 1, N_("Average")},	/* 500B */
		{PTP_DPC_ExposureMeteringMode, 0, 2, N_("Center Weighted Average")},
		{PTP_DPC_ExposureMeteringMode, 0, 3, N_("Multi-spot")},
		{PTP_DPC_ExposureMeteringMode, 0, 4, N_("Center-spot")},
		{PTP_DPC_FlashMode, 0, 0, N_("Undefined")},		/* 500C */
		{PTP_DPC_FlashMode, 0, 1, N_("Automatic flash")},
		{PTP_DPC_FlashMode, 0, 2, N_("Flash off")},
		{PTP_DPC_FlashMode, 0, 3, N_("Fill flash")},
		{PTP_DPC_FlashMode, 0, 4, N_("Automatic Red-eye Reduction")},
		{PTP_DPC_FlashMode, 0, 5, N_("Red-eye fill flash")},
		{PTP_DPC_FlashMode, 0, 6, N_("External sync")},
		{PTP_DPC_FlashMode, PTP_VENDOR_NIKON, 32784, N_("Auto")},
		{PTP_DPC_FlashMode, PTP_VENDOR_NIKON, 32785, N_("Auto Slow Sync")},
		{PTP_DPC_FlashMode, PTP_VENDOR_NIKON, 32786, N_("Rear Curtain Sync + Slow Sync")},
		{PTP_DPC_FlashMode, PTP_VENDOR_NIKON, 32787, N_("Red-eye Reduction + Slow Sync")},
		{PTP_DPC_ExposureProgramMode, 0, 1, "M"},		/* 500E */
		{PTP_DPC_ExposureProgramMode, 0, 3, "A"},
		{PTP_DPC_ExposureProgramMode, 0, 4, "S"},
		{PTP_DPC_ExposureProgramMode, 0, 2, "P"},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32784, N_("Auto")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32785, N_("Portrait")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32786, N_("Landscape")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32787, N_("Macro")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32788, N_("Sports")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32790, N_("Night Landscape")},
		{PTP_DPC_ExposureProgramMode, PTP_VENDOR_NIKON, 32789, N_("Night Portrait")},
		{PTP_DPC_StillCaptureMode, 0, 1, N_("Single Shot")},	/* 5013 */
		{PTP_DPC_StillCaptureMode, 0, 2, N_("Power Wind")},
		{PTP_DPC_StillCaptureMode, 0, 3, N_("Timelapse")},
		{PTP_DPC_StillCaptureMode, PTP_VENDOR_NIKON, 32784, N_("Continuous Low Speed")},
		{PTP_DPC_StillCaptureMode, PTP_VENDOR_NIKON, 32785, N_("Timer")},
		{PTP_DPC_StillCaptureMode, PTP_VENDOR_NIKON, 32787, N_("Remote")},
		{PTP_DPC_StillCaptureMode, PTP_VENDOR_NIKON, 32787, N_("Mirror Up")},
		{PTP_DPC_StillCaptureMode, PTP_VENDOR_NIKON, 32788, N_("Timer + Remote")},
		{PTP_DPC_FocusMeteringMode, 0, 1, N_("Centre-spot")},	/* 501C */
		{PTP_DPC_FocusMeteringMode, 0, 2, N_("Multi-spot")},
		{PTP_DPC_FocusMeteringMode, PTP_VENDOR_NIKON, 32784, N_("Single Area")},
		{PTP_DPC_FocusMeteringMode, PTP_VENDOR_NIKON, 32785, N_("Closest Subject")},
		{PTP_DPC_FocusMeteringMode, PTP_VENDOR_NIKON, 32786, N_("Group Dynamic")},

		{PTP_DPC_VideoFormat, 0, 0x47504a4d, "MJPG"},	/* 5022 */


		/* Nikon specific device properties */
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 0, N_("Auto")},	/* D02A */
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 1, N_("Normal")},
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 2, N_("Low")},
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 3, N_("Medium Low")},
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 4, N_("Medium high")},
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 5, N_("High")},
		{PTP_DPC_NIKON_ImageSharpening, PTP_VENDOR_NIKON, 6, N_("None")},

		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 0, N_("Auto")},	/* D02B */
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 1, N_("Normal")},
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 2, N_("Low contrast")},
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 3, N_("Medium Low")},
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 4, N_("Medium High")},
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 5, N_("High control")},
		{PTP_DPC_NIKON_ToneCompensation, PTP_VENDOR_NIKON, 6, N_("Custom")},

		{PTP_DPC_NIKON_ColorModel, PTP_VENDOR_NIKON, 0, "sRGB"},		/* D02C */
		{PTP_DPC_NIKON_ColorModel, PTP_VENDOR_NIKON, 1, "AdobeRGB"},
		{PTP_DPC_NIKON_ColorModel, PTP_VENDOR_NIKON, 2, "sRGB"},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_AutoDXCrop,PTP_VENDOR_NIKON),	   	/* D033 */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_FocusAreaWrap,PTP_VENDOR_NIKON),   	/* D04F */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_EnableCopyright,PTP_VENDOR_NIKON),   	/* D053 */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_ISOAuto,PTP_VENDOR_NIKON),	   	/* D054 */

		/* FIXME! this is not ISO Auto (which is a bool) Perhaps ISO Auto Time?*/
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 0, "1/125"},			/* D054 */
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 1, "1/60"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 2, "1/30"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 3, "1/15"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 4, "1/8"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 5, "1/4"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 6, "1/2"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 7, "1"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 8, "2"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 9, "4"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 10, "8"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 11, "15"},
		{PTP_DPC_NIKON_ISOAuto,	PTP_VENDOR_NIKON, 12, "30"},

		{PTP_DPC_NIKON_EVStep, PTP_VENDOR_NIKON, 0, "1/3"},			/* D056 */
		{PTP_DPC_NIKON_EVStep, PTP_VENDOR_NIKON, 1, "1/2"},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_ExposureCompensation,PTP_VENDOR_NIKON),/*D058 */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_AELockMode,PTP_VENDOR_NIKON),    	/* D05E */

		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 0, N_("AE/AF Lock")},	/* D05F */
		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 1, N_("AF Lock only")},
		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 2, N_("AE Lock only")},
		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 3, N_("AF Lock Hold")},
		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 4, N_("AF On")},
		{PTP_DPC_NIKON_AELAFLMode, PTP_VENDOR_NIKON, 5, N_("Flash Lock")},

		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 0, N_("4 seconds")},		/* D062 */
		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 1, N_("6 seconds")},
		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 2, N_("8 seconds")},
		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 3, N_("16 seconds")},
		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 4, N_("30 minutes")},
		{PTP_DPC_NIKON_MeterOff, PTP_VENDOR_NIKON, 5, N_("30 seconds")},

		{PTP_DPC_NIKON_SelfTimer, PTP_VENDOR_NIKON, 0, N_("2 seconds")},	/* D063 */
		{PTP_DPC_NIKON_SelfTimer, PTP_VENDOR_NIKON, 1, N_("5 seconds")},
		{PTP_DPC_NIKON_SelfTimer, PTP_VENDOR_NIKON, 2, N_("10 seconds")},
		{PTP_DPC_NIKON_SelfTimer, PTP_VENDOR_NIKON, 3, N_("20 seconds")},

		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 0, N_("10 seconds")},	/* D064 */
		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 1, N_("20 seconds")},
		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 2, N_("1 minute")},
		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 3, N_("5 minutes")},
		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 4, N_("10 minutes")},
		{PTP_DPC_NIKON_MonitorOff, PTP_VENDOR_NIKON, 5, N_("5 seconds")}, /* d80 observed */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_ExposureDelayMode,PTP_VENDOR_NIKON),	/* D06A */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_LongExposureNoiseReduction,PTP_VENDOR_NIKON),	/* D06B */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_FileNumberSequence,PTP_VENDOR_NIKON),	/* D06C */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_D7Illumination,PTP_VENDOR_NIKON),	/* D06F */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_SHSET_CH_GUID_DISP,PTP_VENDOR_NIKON),	/* D071 */

		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 0, "1/60s"},		/* D075 */
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 1, "1/30s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 2, "1/15s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 3, "1/8s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 4, "1/4s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 5, "1/2s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 6, "1s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 7, "2s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 8, "4s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 9, "8s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 10, "15s"},
		{PTP_DPC_NIKON_FlashShutterSpeed, PTP_VENDOR_NIKON, 11, "30s"},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_E4ModelingFlash,PTP_VENDOR_NIKON),	/* D077 */

		{PTP_DPC_NIKON_BracketSet, PTP_VENDOR_NIKON, 0, N_("AE & Flash")},	/* D078 */
		{PTP_DPC_NIKON_BracketSet, PTP_VENDOR_NIKON, 1, N_("AE only")},
		{PTP_DPC_NIKON_BracketSet, PTP_VENDOR_NIKON, 2, N_("Flash only")},
		{PTP_DPC_NIKON_BracketSet, PTP_VENDOR_NIKON, 3, N_("WB bracketing")},

		{PTP_DPC_NIKON_BracketOrder, PTP_VENDOR_NIKON, 0, N_("MTR > Under")},	/* D07A */
		{PTP_DPC_NIKON_BracketOrder, PTP_VENDOR_NIKON, 1, N_("Under > MTR")},

		{PTP_DPC_NIKON_F1CenterButtonShootingMode, PTP_VENDOR_NIKON, 0, N_("Reset focus point to center")}, /* D080 */
		{PTP_DPC_NIKON_F1CenterButtonShootingMode, PTP_VENDOR_NIKON, 1, N_("Highlight active focus point")},
		{PTP_DPC_NIKON_F1CenterButtonShootingMode, PTP_VENDOR_NIKON, 2, N_("Unused")},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_F3PhotoInfoPlayback,PTP_VENDOR_NIKON),/* D083 */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_F5CustomizeCommDials,PTP_VENDOR_NIKON),/* D085 */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_ReverseCommandDial,PTP_VENDOR_NIKON),	/* D086 */
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_F6ButtonsAndDials,PTP_VENDOR_NIKON),	/* D089 */
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_NoCFCard,PTP_VENDOR_NIKON),		/* D08A */
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_AFAreaPoint,PTP_VENDOR_NIKON),	/* D08D */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_ImageCommentEnable,PTP_VENDOR_NIKON),	/* D091 */
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_ImageRotation,PTP_VENDOR_NIKON),	/* D092 */

		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_MovVoice,PTP_VENDOR_NIKON),		/* D0A1 */

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_Bracketing,PTP_VENDOR_NIKON),		/* D0C0 */

		/* http://www.rottmerhusen.com/objektives/lensid/nikkor.html is complete */
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 0, N_("Unknown")},		/* D0E0 */
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 38, "Sigma 70-300mm 1:4-5.6 D APO Macro"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 83, "AF Nikkor 80-200mm 1:2.8 D ED"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 118, "AF Nikkor 50mm 1:1.8 D"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 127, "AF-S Nikkor 18-70mm 1:3.5-4.5G ED DX"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 139, "AF-S Nikkor 18-200mm 1:3.5-5.6 GED DX VR"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 147, "AF-S Nikkor 24-70mm 1:2.8G ED DX"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 154, "AF-S Nikkor 18-55mm 1:3.5-F5.6G DX VR"},
		{PTP_DPC_NIKON_LensID, PTP_VENDOR_NIKON, 159, "AF-S Nikkor 35mm 1:1.8G DX"},
		{PTP_DPC_NIKON_FinderISODisp, PTP_VENDOR_NIKON, 0, "Show ISO sensitivity"},/* 0xD0F0 */
		{PTP_DPC_NIKON_FinderISODisp, PTP_VENDOR_NIKON, 1, "Show ISO/Easy ISO"},
		{PTP_DPC_NIKON_FinderISODisp, PTP_VENDOR_NIKON, 2, "Show frame count"},

		{PTP_DPC_NIKON_RawCompression, PTP_VENDOR_NIKON, 0, N_("Lossless")},	/* D016 */
		{PTP_DPC_NIKON_RawCompression, PTP_VENDOR_NIKON, 1, N_("Lossy")},

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ACPower,PTP_VENDOR_NIKON),		/* D101 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_AFLockStatus,PTP_VENDOR_NIKON),		/* D104 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_AELockStatus,PTP_VENDOR_NIKON),		/* D105 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_FVLockStatus,PTP_VENDOR_NIKON),		/* D106 */

		{PTP_DPC_NIKON_AutofocusArea, PTP_VENDOR_NIKON, 0, N_("Centre")},	/* D108 */
		{PTP_DPC_NIKON_AutofocusArea, PTP_VENDOR_NIKON, 1, N_("Top")},
		{PTP_DPC_NIKON_AutofocusArea, PTP_VENDOR_NIKON, 2, N_("Bottom")},
		{PTP_DPC_NIKON_AutofocusArea, PTP_VENDOR_NIKON, 3, N_("Left")},
		{PTP_DPC_NIKON_AutofocusArea, PTP_VENDOR_NIKON, 4, N_("Right")},

		{PTP_DPC_NIKON_RecordingMedia, PTP_VENDOR_NIKON, 0, N_("Card")},	/* D10B */
		{PTP_DPC_NIKON_RecordingMedia, PTP_VENDOR_NIKON, 1, N_("SDRam")},

		{PTP_DPC_NIKON_USBSpeed, PTP_VENDOR_NIKON, 0, N_("USB 1.1")},		/* D10C */
		{PTP_DPC_NIKON_USBSpeed, PTP_VENDOR_NIKON, 1, N_("USB 2.0")},

		{PTP_DPC_NIKON_CameraOrientation, PTP_VENDOR_NIKON, 0, "0'"},		/* D10E */
		{PTP_DPC_NIKON_CameraOrientation, PTP_VENDOR_NIKON, 1, "270'"},
		{PTP_DPC_NIKON_CameraOrientation, PTP_VENDOR_NIKON, 2, "90'"},
		{PTP_DPC_NIKON_CameraOrientation, PTP_VENDOR_NIKON, 3, "180'"},

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_FNumberLock,PTP_VENDOR_NIKON),		/* D110 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ExposureApertureLock,PTP_VENDOR_NIKON),	/* D111 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_TVLockSetting,PTP_VENDOR_NIKON),	/* D112 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_AVLockSetting,PTP_VENDOR_NIKON),	/* D113 */

		{PTP_DPC_NIKON_IllumSetting,PTP_VENDOR_NIKON,0,N_("LCD Backlight")},	/* D114 */
		{PTP_DPC_NIKON_IllumSetting,PTP_VENDOR_NIKON,1,N_("LCD Backlight and Info Display")},

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ExternalFlashAttached,PTP_VENDOR_NIKON),/* D120 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ExternalFlashStatus,PTP_VENDOR_NIKON),	/* D121 */

		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 0, N_("Normal")},	/* D140 */
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 1, N_("Vivid")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 2, N_("Sharper")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 3, N_("Softer")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 4, N_("Direct Print")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 5, N_("Portrait")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 6, N_("Landscape")},
		{PTP_DPC_NIKON_OptimizeImage, PTP_VENDOR_NIKON, 7, N_("Custom")},

		{PTP_DPC_NIKON_Saturation, PTP_VENDOR_NIKON, 0, N_("Normal")},		/* D142 */
		{PTP_DPC_NIKON_Saturation, PTP_VENDOR_NIKON, 1, N_("Moderate")},
		{PTP_DPC_NIKON_Saturation, PTP_VENDOR_NIKON, 2, N_("Enhanced")},

		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_BeepOff,PTP_VENDOR_NIKON),		/* D160 */

		{PTP_DPC_NIKON_AutofocusMode, PTP_VENDOR_NIKON, 0, N_("AF-S")},	 	/* D161 */
		{PTP_DPC_NIKON_AutofocusMode, PTP_VENDOR_NIKON, 1, N_("AF-C")},
		{PTP_DPC_NIKON_AutofocusMode, PTP_VENDOR_NIKON, 2, N_("AF-A")},
		{PTP_DPC_NIKON_AutofocusMode, PTP_VENDOR_NIKON, 3, N_("MF (fixed)")},
		{PTP_DPC_NIKON_AutofocusMode, PTP_VENDOR_NIKON, 4, N_("MF (selection)")},

		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_AFAssist,PTP_VENDOR_NIKON),   	/* D163 */

		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 0,  "1/125"},		/* D164 */
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 1,  "1/60"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 2,  "1/30"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 3,  "1/15"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 4,  "1/8"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 5,  "1/4"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 6,  "1/2"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 7,  "1"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 8,  "2"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 9,  "4"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 10, "8"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 11, "15"},
		{PTP_DPC_NIKON_PADVPMode, PTP_VENDOR_NIKON, 12, "30"},

		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_ImageReview,PTP_VENDOR_NIKON),	/* D165 */

		{PTP_DPC_NIKON_AFAreaIllumination, PTP_VENDOR_NIKON, 0, N_("Auto")},	/* D166 */
		{PTP_DPC_NIKON_AFAreaIllumination, PTP_VENDOR_NIKON, 1, N_("Off")},
		{PTP_DPC_NIKON_AFAreaIllumination, PTP_VENDOR_NIKON, 2, N_("On")},

		{PTP_DPC_NIKON_FlashMode, PTP_VENDOR_NIKON, 0, "iTTL"},			/* D167 */
		{PTP_DPC_NIKON_FlashMode, PTP_VENDOR_NIKON, 1, N_("Manual")},
		{PTP_DPC_NIKON_FlashMode, PTP_VENDOR_NIKON, 2, N_("Commander")},

		{PTP_DPC_NIKON_FlashCommanderMode, PTP_VENDOR_NIKON, 0, N_("TTL")},	/* D168 */
		{PTP_DPC_NIKON_FlashCommanderMode, PTP_VENDOR_NIKON, 1, N_("Auto Aperture")},
		{PTP_DPC_NIKON_FlashCommanderMode, PTP_VENDOR_NIKON, 2, N_("Full Manual")},

		PTP_VENDOR_VAL_RBOOL(PTP_DPC_NIKON_FlashSign,PTP_VENDOR_NIKON),		/* D169 */

		{PTP_DPC_NIKON_RemoteTimeout, PTP_VENDOR_NIKON, 0, N_("1 min")},	/* D16B */
		{PTP_DPC_NIKON_RemoteTimeout, PTP_VENDOR_NIKON, 1, N_("5 mins")},
		{PTP_DPC_NIKON_RemoteTimeout, PTP_VENDOR_NIKON, 2, N_("10 mins")},
		{PTP_DPC_NIKON_RemoteTimeout, PTP_VENDOR_NIKON, 3, N_("15 mins")},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_GridDisplay,PTP_VENDOR_NIKON),	/* D16C */

		{PTP_DPC_NIKON_FlashModeManualPower, PTP_VENDOR_NIKON, 0, N_("Full")},	/* D16D */
		{PTP_DPC_NIKON_FlashModeManualPower, PTP_VENDOR_NIKON, 1, "1/2"},
		{PTP_DPC_NIKON_FlashModeManualPower, PTP_VENDOR_NIKON, 2, "1/4"},
		{PTP_DPC_NIKON_FlashModeManualPower, PTP_VENDOR_NIKON, 3, "1/8"},
		{PTP_DPC_NIKON_FlashModeManualPower, PTP_VENDOR_NIKON, 4, "1/16"},

		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 0, N_("Full")},/* D16E */
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 1, "1/2"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 2, "1/4"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 3, "1/8"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 4, "1/16"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 5, "1/32"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 6, "1/64"},
		{PTP_DPC_NIKON_FlashModeCommanderPower, PTP_VENDOR_NIKON, 7, "1/128"},

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_CSMMenu,PTP_VENDOR_NIKON),		/* D180 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_WarningDisplay,PTP_VENDOR_NIKON),	/* D181 */

		{PTP_DPC_NIKON_BatteryCellKind, PTP_VENDOR_NIKON, 0, "LR6 (AA alkaline)"},/* D182 */
		{PTP_DPC_NIKON_BatteryCellKind, PTP_VENDOR_NIKON, 1, "HR6 (AA Ni-Mh)"},
		{PTP_DPC_NIKON_BatteryCellKind, PTP_VENDOR_NIKON, 2, "FR6 (AA Lithium)"},
		{PTP_DPC_NIKON_BatteryCellKind, PTP_VENDOR_NIKON, 3, "ZR6 (AA Ni-Mn)"},

		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 0, "400"},		/* D183 */
		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 1, "800"},
		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 2, "1600"},
		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 3, "3200"},
		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 4, "Hi 1"},
		{PTP_DPC_NIKON_ISOAutoHiLimit, PTP_VENDOR_NIKON, 5, "Hi 2"},

		{PTP_DPC_NIKON_InfoDispSetting, PTP_VENDOR_NIKON, 0, N_("Auto")},	/* 0xD187 */
		{PTP_DPC_NIKON_InfoDispSetting, PTP_VENDOR_NIKON, 1, N_("Dark on light")},
		{PTP_DPC_NIKON_InfoDispSetting, PTP_VENDOR_NIKON, 2, N_("Light on dark")},

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_IndicatorDisp,PTP_VENDOR_NIKON),	/* D18D */

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_LiveViewStatus,PTP_VENDOR_NIKON),	/* D1A2 */

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ExposureDisplayStatus,PTP_VENDOR_NIKON),/* D1B0 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_InfoDispErrStatus,PTP_VENDOR_NIKON),	/* D1B2 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ExposureIndicateLightup,PTP_VENDOR_NIKON),/* D1B3 */

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_FlashOpen,PTP_VENDOR_NIKON),		/* D1C0 */
		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_FlashCharged,PTP_VENDOR_NIKON),		/* D1C1 */

		PTP_VENDOR_VAL_YN(PTP_DPC_NIKON_ManualMovieSetting,PTP_VENDOR_NIKON),	/* 0xD0A6 */

		{PTP_DPC_NIKON_FlickerReduction, PTP_VENDOR_NIKON, 0, "50Hz"},		/* 0xD034 */
		{PTP_DPC_NIKON_FlickerReduction, PTP_VENDOR_NIKON, 1, "60Hz"},

		{PTP_DPC_NIKON_RemoteMode, PTP_VENDOR_NIKON, 0, N_("Delayed Remote")},	/* 0xD035 */
		{PTP_DPC_NIKON_RemoteMode, PTP_VENDOR_NIKON, 1, N_("Quick Response")},	/* 0xD035 */
		{PTP_DPC_NIKON_RemoteMode, PTP_VENDOR_NIKON, 2, N_("Remote Mirror Up")},/* 0xD035 */

		{PTP_DPC_NIKON_MonitorOffDelay, PTP_VENDOR_NIKON, 0, "5min"},	/* 0xD0b3 */
		{PTP_DPC_NIKON_MonitorOffDelay, PTP_VENDOR_NIKON, 1, "10min"},	/* 0xD0b3 */
		{PTP_DPC_NIKON_MonitorOffDelay, PTP_VENDOR_NIKON, 2, "15min"},	/* 0xD0b3 */
		{PTP_DPC_NIKON_MonitorOffDelay, PTP_VENDOR_NIKON, 3, "20min"},	/* 0xD0b3 */
		{PTP_DPC_NIKON_MonitorOffDelay, PTP_VENDOR_NIKON, 4, "30min"},	/* 0xD0b3 */


		/* Canon stuff */
		PTP_VENDOR_VAL_BOOL(PTP_DPC_CANON_AssistLight,PTP_VENDOR_CANON),
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_CANON_RotationScene,PTP_VENDOR_CANON),
		PTP_VENDOR_VAL_RBOOL(PTP_DPC_CANON_BeepMode,PTP_VENDOR_CANON),
		PTP_VENDOR_VAL_BOOL(PTP_DPC_CANON_Beep,PTP_VENDOR_CANON),

		{PTP_DPC_CANON_RotationAngle, PTP_VENDOR_CANON, 0, "0'"},
		{PTP_DPC_CANON_RotationAngle, PTP_VENDOR_CANON, 3, "270'"},
		{PTP_DPC_CANON_RotationAngle, PTP_VENDOR_CANON, 1, "90'"},

		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 0, N_("Unknown")},
		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 1, N_("AC")},
		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 2, N_("Lithium Ion")},
		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 3, N_("Nickel hydride")},
		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 4, N_("Nickel cadmium")},
		{PTP_DPC_CANON_BatteryKind, PTP_VENDOR_CANON, 5, N_("Alkalium manganese")},

		{PTP_DPC_CANON_BatteryStatus, PTP_VENDOR_CANON, 0, N_("Undefined")},
		{PTP_DPC_CANON_BatteryStatus, PTP_VENDOR_CANON, 1, N_("Normal")},
		{PTP_DPC_CANON_BatteryStatus, PTP_VENDOR_CANON, 2, N_("Warning Level 1")},
		{PTP_DPC_CANON_BatteryStatus, PTP_VENDOR_CANON, 3, N_("Emergency")},
		{PTP_DPC_CANON_BatteryStatus, PTP_VENDOR_CANON, 4, N_("Warning Level 0")},

		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 0, N_("Undefined")},
		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 1, N_("Economy")},
		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 2, N_("Normal")},
		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 3, N_("Fine")},
		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 4, N_("Lossless")},
		{PTP_DPC_CANON_ImageQuality, PTP_VENDOR_CANON, 5, N_("SuperFine")},

		{PTP_DPC_CANON_FullViewFileFormat, PTP_VENDOR_CANON, 0, N_("Undefined")},
		{PTP_DPC_CANON_FullViewFileFormat, PTP_VENDOR_CANON, 1, N_("JPEG")},
		{PTP_DPC_CANON_FullViewFileFormat, PTP_VENDOR_CANON, 2, N_("CRW")},

		{PTP_DPC_CANON_ImageSize, PTP_VENDOR_CANON, 0, N_("Large")},
		{PTP_DPC_CANON_ImageSize, PTP_VENDOR_CANON, 1, N_("Medium 1")},
		{PTP_DPC_CANON_ImageSize, PTP_VENDOR_CANON, 2, N_("Small")},
		{PTP_DPC_CANON_ImageSize, PTP_VENDOR_CANON, 3, N_("Medium 2")},
		{PTP_DPC_CANON_ImageSize, PTP_VENDOR_CANON, 7, N_("Medium 3")},

		{PTP_DPC_CANON_SelfTime, PTP_VENDOR_CANON, 0,   N_("Not used")},
		{PTP_DPC_CANON_SelfTime, PTP_VENDOR_CANON, 100, N_("10 seconds")},
		{PTP_DPC_CANON_SelfTime, PTP_VENDOR_CANON, 20,  N_("2 seconds")},

		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 0,  N_("Off")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 1,  N_("Auto")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 2,  N_("On")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 3,  N_("Red Eye Suppression")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 4,  N_("Low Speed Synchronization")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 5,  N_("Auto + Red Eye Suppression")},
		{PTP_DPC_CANON_FlashMode, PTP_VENDOR_CANON, 6,  N_("On + Red Eye Suppression")},

		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 0,  N_("Auto")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 1,  N_("P")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 2,  N_("Tv")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 3,  N_("Av")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 4,  N_("M")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 5,  N_("A_DEP")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 6,  N_("M_DEP")},
		{PTP_DPC_CANON_ShootingMode, PTP_VENDOR_CANON, 7,  N_("Bulb")},
		/* more actually */

		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 0,  N_("Auto")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 1,  N_("Manual")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 2,  N_("Distant View")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 3,  N_("High-Speed Shutter")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 4,  N_("Low-Speed Shutter")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 5,  N_("Night View")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 6,  N_("Grayscale")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 7,  N_("Sepia")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 8,  N_("Portrait")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 9,  N_("Sports")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 10,  N_("Macro")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 11,  N_("Monochrome")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 12,  N_("Pan Focus")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 13,  N_("Neutral")},
		{PTP_DPC_CANON_ImageMode, PTP_VENDOR_CANON, 14,  N_("Soft")},

		{PTP_DPC_CANON_DriveMode, PTP_VENDOR_CANON, 0,  N_("Single-Frame Shooting")},
		{PTP_DPC_CANON_DriveMode, PTP_VENDOR_CANON, 1,  N_("Continuous Shooting")},
		{PTP_DPC_CANON_DriveMode, PTP_VENDOR_CANON, 2,  N_("Timer (Single) Shooting")},
		{PTP_DPC_CANON_DriveMode, PTP_VENDOR_CANON, 4,  N_("Continuous Low-speed Shooting")},
		{PTP_DPC_CANON_DriveMode, PTP_VENDOR_CANON, 5,  N_("Continuous High-speed Shooting")},

		{PTP_DPC_CANON_EZoom, PTP_VENDOR_CANON, 0,  N_("Off")},
		{PTP_DPC_CANON_EZoom, PTP_VENDOR_CANON, 1,  N_("2x")},
		{PTP_DPC_CANON_EZoom, PTP_VENDOR_CANON, 2,  N_("4x")},
		{PTP_DPC_CANON_EZoom, PTP_VENDOR_CANON, 3,  N_("Smooth")},

		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 0,  N_("Center-weighted Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 1,  N_("Spot Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 2,  N_("Average Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 3,  N_("Evaluative Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 4,  N_("Partial Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 5,  N_("Center-weighted Average Metering")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 6,  N_("Spot Metering Interlocked with AF Frame")},
		{PTP_DPC_CANON_MeteringMode, PTP_VENDOR_CANON, 7,  N_("Multi-Spot Metering")},

		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 0,  N_("Manual")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 1,  N_("Auto")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 2,  N_("Unknown")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 3,  N_("Zone Focus (Close-up)")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 4,  N_("Zone Focus (Very Close)")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 5,  N_("Zone Focus (Close)")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 6,  N_("Zone Focus (Medium)")},
		{PTP_DPC_CANON_AFDistance, PTP_VENDOR_CANON, 7,  N_("Zone Focus (Far)")},

		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0,  N_("Invalid")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x1000,  N_("Focusing Point on Center Only, Manual")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x1001,  N_("Focusing Point on Center Only, Auto")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x3000,  N_("Multiple Focusing Points (No Specification), Manual")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x3001,  N_("Multiple Focusing Points, Auto")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x3002,  N_("Multiple Focusing Points (Right)")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x3003,  N_("Multiple Focusing Points (Center)")},
		{PTP_DPC_CANON_FocusingPoint, PTP_VENDOR_CANON, 0x3004,  N_("Multiple Focusing Points (Left)")},

		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 0,  N_("Auto")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 1,  N_("Daylight")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 2,  N_("Cloudy")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 3,  N_("Tungsten")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 4,  N_("Fluorescent")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 6,  N_("Preset")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 7,  N_("Fluorescent H")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 9,  N_("Color Temperature")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 10,  N_("Custom Whitebalance PC-1")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 11,  N_("Custom Whitebalance PC-2")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 12,  N_("Custom Whitebalance PC-3")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 13,  N_("Missing Number")},
		{PTP_DPC_CANON_WhiteBalance, PTP_VENDOR_CANON, 14,  N_("Fluorescent H")}, /* dup? */

		{PTP_DPC_CANON_SlowShutterSetting, PTP_VENDOR_CANON, 0,  N_("Off")},
		{PTP_DPC_CANON_SlowShutterSetting, PTP_VENDOR_CANON, 1,  N_("Night View")},
		{PTP_DPC_CANON_SlowShutterSetting, PTP_VENDOR_CANON, 2,  N_("On")},
		{PTP_DPC_CANON_SlowShutterSetting, PTP_VENDOR_CANON, 3,  N_("Low-speed shutter function not available")},

		{PTP_DPC_CANON_AFMode, PTP_VENDOR_CANON, 0,  N_("Single Shot")},
		{PTP_DPC_CANON_AFMode, PTP_VENDOR_CANON, 1,  N_("AI Servo")},
		{PTP_DPC_CANON_AFMode, PTP_VENDOR_CANON, 2,  N_("AI Focus")},
		{PTP_DPC_CANON_AFMode, PTP_VENDOR_CANON, 3,  N_("Manual")},
		{PTP_DPC_CANON_AFMode, PTP_VENDOR_CANON, 4,  N_("Continuous")},

		PTP_VENDOR_VAL_BOOL(PTP_DPC_CANON_ImageStabilization,PTP_VENDOR_CANON),

		{PTP_DPC_CANON_Contrast, PTP_VENDOR_CANON, -2,  N_("Low 2")},
		{PTP_DPC_CANON_Contrast, PTP_VENDOR_CANON, -1,  N_("Low")},
		{PTP_DPC_CANON_Contrast, PTP_VENDOR_CANON, 0,  N_("Standard")},
		{PTP_DPC_CANON_Contrast, PTP_VENDOR_CANON, 1,  N_("High")},
		{PTP_DPC_CANON_Contrast, PTP_VENDOR_CANON, 2,  N_("High 2")},

		{PTP_DPC_CANON_ColorGain, PTP_VENDOR_CANON, -2,  N_("Low 2")},
		{PTP_DPC_CANON_ColorGain, PTP_VENDOR_CANON, -1,  N_("Low")},
		{PTP_DPC_CANON_ColorGain, PTP_VENDOR_CANON, 0,  N_("Standard")},
		{PTP_DPC_CANON_ColorGain, PTP_VENDOR_CANON, 1,  N_("High")},
		{PTP_DPC_CANON_ColorGain, PTP_VENDOR_CANON, 2,  N_("High 2")},

		{PTP_DPC_CANON_Sharpness, PTP_VENDOR_CANON, -2,  N_("Low 2")},
		{PTP_DPC_CANON_Sharpness, PTP_VENDOR_CANON, -1,  N_("Low")},
		{PTP_DPC_CANON_Sharpness, PTP_VENDOR_CANON, 0,  N_("Standard")},
		{PTP_DPC_CANON_Sharpness, PTP_VENDOR_CANON, 1,  N_("High")},
		{PTP_DPC_CANON_Sharpness, PTP_VENDOR_CANON, 2,  N_("High 2")},

		{PTP_DPC_CANON_Sensitivity, PTP_VENDOR_CANON, 0,  N_("Standard")},
		{PTP_DPC_CANON_Sensitivity, PTP_VENDOR_CANON, 1,  N_("Upper 1")},
		{PTP_DPC_CANON_Sensitivity, PTP_VENDOR_CANON, 2,  N_("Upper 2")},

		{PTP_DPC_CANON_ParameterSet, PTP_VENDOR_CANON, 0x08,  N_("Standard Development Parameters")},
		{PTP_DPC_CANON_ParameterSet, PTP_VENDOR_CANON, 0x10,  N_("Development Parameters 1")},
		{PTP_DPC_CANON_ParameterSet, PTP_VENDOR_CANON, 0x20,  N_("Development Parameters 2")},
		{PTP_DPC_CANON_ParameterSet, PTP_VENDOR_CANON, 0x40,  N_("Development Parameters 3")},

		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x00,  N_("Auto")},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x28,  "6"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x30,  "12"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x38,  "25"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x40,  "50"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x43,  "64"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x48,  "100"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x50,  "200"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x58,  "400"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x60,  "800"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x68,  "1600"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x70,  "3200"},
		{PTP_DPC_CANON_ISOSpeed, PTP_VENDOR_CANON, 0x78,  "6400"},

		/* 0xd01d - PTP_DPC_CANON_Aperture */
		/* 0xd01e - PTP_DPC_CANON_ShutterSpeed */
		/* 0xd01f - PTP_DPC_CANON_ExpCompensation */
		/* 0xd020 - PTP_DPC_CANON_FlashCompensation */
		/* 0xd021 - PTP_DPC_CANON_AEBExposureCompensation */
		/* 0xd023 - PTP_DPC_CANON_AvOpen */
		/* 0xd024 - PTP_DPC_CANON_AvMax */

		{PTP_DPC_CANON_CameraOutput, PTP_VENDOR_CANON, 0,  N_("Undefined")},
		{PTP_DPC_CANON_CameraOutput, PTP_VENDOR_CANON, 1,  N_("LCD")},
		{PTP_DPC_CANON_CameraOutput, PTP_VENDOR_CANON, 2,  N_("Video OUT")},
		{PTP_DPC_CANON_CameraOutput, PTP_VENDOR_CANON, 3,  N_("Off")},

		{PTP_DPC_CANON_MlSpotPos, PTP_VENDOR_CANON, 0, N_("MlSpotPosCenter")},
		{PTP_DPC_CANON_MlSpotPos, PTP_VENDOR_CANON, 1, N_("MlSpotPosAfLink")},

		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 0, N_("Off")},
		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 1, N_("Vivid")},
		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 2, N_("Neutral")},
		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 3, N_("Soft")},
		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 4, N_("Sepia")},
		{PTP_DPC_CANON_PhotoEffect, PTP_VENDOR_CANON, 5, N_("Monochrome")},

		{0, 0, 0, NULL}
	};
	for (i=0; ptp_value_trans[i].dpc!=0; i++) {
		if ((ptp_value_trans[i].dpc == dpc) &&
			(((ptp_value_trans[i].dpc & 0xf000) == 0x5000) ||
			 (ptp_value_trans[i].vendor == params->deviceinfo.VendorExtensionID))
		) {
			double value = _value_to_num(&(dpd->CurrentValue), dpd->DataType);

			return snprintf(out, length,
				_(ptp_value_trans[i].format),
				value * ptp_value_trans[i].coef +
				ptp_value_trans[i].bias);
		}
	}

	kval = _value_to_num(&(dpd->CurrentValue), dpd->DataType);
	for (i=0; ptp_value_list[i].dpc!=0; i++) {
		if ((ptp_value_list[i].dpc == dpc) &&
			(((ptp_value_list[i].dpc & 0xf000) == 0x5000) ||
			  (ptp_value_list[i].vendor == params->deviceinfo.VendorExtensionID)) &&
		    (ptp_value_list[i].key==kval)
		) {
			return snprintf(out, length, "%s", _(ptp_value_list[i].value));
		}
	}
	if (params->deviceinfo.VendorExtensionID==PTP_VENDOR_MICROSOFT
	    || params->deviceinfo.VendorExtensionID==PTP_VENDOR_MTP
	    || params->deviceinfo.VendorExtensionID==PTP_VENDOR_PANASONIC) {
		switch (dpc) {
		case PTP_DPC_MTP_SynchronizationPartner:
		case PTP_DPC_MTP_DeviceFriendlyName:
			if (dpd->DataType == PTP_DTC_STR)
				return snprintf(out, length, "%s", dpd->CurrentValue.str);
			else
				return snprintf(out, length, "invalid type, expected STR");
		case PTP_DPC_MTP_SecureTime:
		case PTP_DPC_MTP_DeviceCertificate: {
			if (dpd->DataType != PTP_DTC_AUINT16)
				return snprintf(out, length, "invalid type, expected AUINT16");
			/* FIXME: Convert to use unicode demux functions */
			for (i=0;(i<dpd->CurrentValue.a.count) && (i<length);i++)
				out[i] = dpd->CurrentValue.a.v[i].u16;
			if (	dpd->CurrentValue.a.count &&
				(dpd->CurrentValue.a.count < length)) {
				out[dpd->CurrentValue.a.count-1] = 0;
				return dpd->CurrentValue.a.count-1;
			} else {
				out[length-1] = 0;
				return length;
			}
			break;
		}
		default:
			break;
		}
	}

	return 0;
}

struct {
	uint16_t ofc;
	const char *format;
} ptp_ofc_trans[] = {
	{PTP_OFC_Undefined,"Undefined Type"},
	{PTP_OFC_Defined,"Defined Type"},
	{PTP_OFC_Association,"Association/Directory"},
	{PTP_OFC_Script,"Script"},
	{PTP_OFC_Executable,"Executable"},
	{PTP_OFC_Text,"Text"},
	{PTP_OFC_HTML,"HTML"},
	{PTP_OFC_DPOF,"DPOF"},
	{PTP_OFC_AIFF,"AIFF"},
	{PTP_OFC_WAV,"MS Wave"},
	{PTP_OFC_MP3,"MP3"},
	{PTP_OFC_AVI,"MS AVI"},
	{PTP_OFC_MPEG,"MPEG"},
	{PTP_OFC_ASF,"ASF"},
	{PTP_OFC_QT,"Apple Quicktime"},
	{PTP_OFC_EXIF_JPEG,"JPEG"},
	{PTP_OFC_TIFF_EP,"TIFF EP"},
	{PTP_OFC_FlashPix,"FlashPix"},
	{PTP_OFC_BMP,"BMP"},
	{PTP_OFC_CIFF,"CIFF"},
	{PTP_OFC_GIF,"GIF"},
	{PTP_OFC_JFIF,"JFIF"},
	{PTP_OFC_PCD,"PCD"},
	{PTP_OFC_PICT,"PICT"},
	{PTP_OFC_PNG,"PNG"},
	{PTP_OFC_TIFF,"TIFF"},
	{PTP_OFC_TIFF_IT,"TIFF_IT"},
	{PTP_OFC_JP2,"JP2"},
	{PTP_OFC_JPX,"JPX"},
	{PTP_OFC_DNG,"DNG"},
};

struct {
	uint16_t ofc;
	const char *format;
} ptp_ofc_mtp_trans[] = {
	{PTP_OFC_MTP_MediaCard,N_("Media Card")},
	{PTP_OFC_MTP_MediaCardGroup,N_("Media Card Group")},
	{PTP_OFC_MTP_Encounter,N_("Encounter")},
	{PTP_OFC_MTP_EncounterBox,N_("Encounter Box")},
	{PTP_OFC_MTP_M4A,N_("M4A")},
	{PTP_OFC_MTP_Firmware,N_("Firmware")},
	{PTP_OFC_MTP_WindowsImageFormat,N_("Windows Image Format")},
	{PTP_OFC_MTP_UndefinedAudio,N_("Undefined Audio")},
	{PTP_OFC_MTP_WMA,"WMA"},
	{PTP_OFC_MTP_OGG,"OGG"},
	{PTP_OFC_MTP_AAC,"AAC"},
	{PTP_OFC_MTP_AudibleCodec,N_("Audible.com Codec")},
	{PTP_OFC_MTP_FLAC,"FLAC"},
	{PTP_OFC_MTP_SamsungPlaylist,N_("Samsung Playlist")},
	{PTP_OFC_MTP_UndefinedVideo,N_("Undefined Video")},
	{PTP_OFC_MTP_WMV,"WMV"},
	{PTP_OFC_MTP_MP4,"MP4"},
	{PTP_OFC_MTP_MP2,"MP2"},
	{PTP_OFC_MTP_3GP,"3GP"},
	{PTP_OFC_MTP_UndefinedCollection,N_("Undefined Collection")},
	{PTP_OFC_MTP_AbstractMultimediaAlbum,N_("Abstract Multimedia Album")},
	{PTP_OFC_MTP_AbstractImageAlbum,N_("Abstract Image Album")},
	{PTP_OFC_MTP_AbstractAudioAlbum,N_("Abstract Audio Album")},
	{PTP_OFC_MTP_AbstractVideoAlbum,N_("Abstract Video Album")},
	{PTP_OFC_MTP_AbstractAudioVideoPlaylist,N_("Abstract Audio Video Playlist")},
	{PTP_OFC_MTP_AbstractContactGroup,N_("Abstract Contact Group")},
	{PTP_OFC_MTP_AbstractMessageFolder,N_("Abstract Message Folder")},
	{PTP_OFC_MTP_AbstractChapteredProduction,N_("Abstract Chaptered Production")},
	{PTP_OFC_MTP_AbstractAudioPlaylist,N_("Abstract Audio Playlist")},
	{PTP_OFC_MTP_AbstractVideoPlaylist,N_("Abstract Video Playlist")},
	{PTP_OFC_MTP_AbstractMediacast,N_("Abstract Mediacast")},
	{PTP_OFC_MTP_WPLPlaylist,N_("WPL Playlist")},
	{PTP_OFC_MTP_M3UPlaylist,N_("M3U Playlist")},
	{PTP_OFC_MTP_MPLPlaylist,N_("MPL Playlist")},
	{PTP_OFC_MTP_ASXPlaylist,N_("ASX Playlist")},
	{PTP_OFC_MTP_PLSPlaylist,N_("PLS Playlist")},
	{PTP_OFC_MTP_UndefinedDocument,N_("Undefined Document")},
	{PTP_OFC_MTP_AbstractDocument,N_("Abstract Document")},
	{PTP_OFC_MTP_XMLDocument,N_("XMLDocument")},
	{PTP_OFC_MTP_MSWordDocument,N_("Microsoft Word Document")},
	{PTP_OFC_MTP_MHTCompiledHTMLDocument,N_("MHT Compiled HTML Document")},
	{PTP_OFC_MTP_MSExcelSpreadsheetXLS,N_("Microsoft Excel Spreadsheet (.xls)")},
	{PTP_OFC_MTP_MSPowerpointPresentationPPT,N_("Microsoft Powerpoint (.ppt)")},
	{PTP_OFC_MTP_UndefinedMessage,N_("Undefined Message")},
	{PTP_OFC_MTP_AbstractMessage,N_("Abstract Message")},
	{PTP_OFC_MTP_UndefinedContact,N_("Undefined Contact")},
	{PTP_OFC_MTP_AbstractContact,N_("Abstract Contact")},
	{PTP_OFC_MTP_vCard2,N_("vCard2")},
	{PTP_OFC_MTP_vCard3,N_("vCard3")},
	{PTP_OFC_MTP_UndefinedCalendarItem,N_("Undefined Calendar Item")},
	{PTP_OFC_MTP_AbstractCalendarItem,N_("Abstract Calendar Item")},
	{PTP_OFC_MTP_vCalendar1,N_("vCalendar1")},
	{PTP_OFC_MTP_vCalendar2,N_("vCalendar2")},
	{PTP_OFC_MTP_UndefinedWindowsExecutable,N_("Undefined Windows Executable")},
	{PTP_OFC_MTP_MediaCast,N_("Media Cast")},
	{PTP_OFC_MTP_Section,N_("Section")},
};

int
ptp_render_ofc(PTPParams* params, uint16_t ofc, int spaceleft, char *txt)
{
	unsigned int i;

	if (!(ofc & 0x8000)) {
		for (i=0;i<ARRAYSIZE(ptp_ofc_trans);i++)
			if (ofc == ptp_ofc_trans[i].ofc)
				return snprintf(txt, spaceleft, "%s", _(ptp_ofc_trans[i].format));
	} else {
		switch (params->deviceinfo.VendorExtensionID) {
		case PTP_VENDOR_EASTMAN_KODAK:
			switch (ofc) {
			case PTP_OFC_EK_M3U:
				return snprintf (txt, spaceleft,"M3U");
			default:
				break;
			}
			break;
		case PTP_VENDOR_CANON:
			switch (ofc) {
			case PTP_OFC_CANON_CRW:
				return snprintf (txt, spaceleft,"CRW");
			default:
				break;
			}
			break;
		case PTP_VENDOR_SONY:
			switch (ofc) {
			case PTP_OFC_SONY_RAW:
				return snprintf (txt, spaceleft,"ARW");
			case PTP_OFC_SONY_MPO:
				return snprintf (txt, spaceleft,"MPO");
			default:
				break;
			}
			break;
		case PTP_VENDOR_FUJI:
			switch (ofc) {
			case PTP_OFC_FUJI_RAF:
				return snprintf (txt, spaceleft,"RAF");
			default:
				break;
			}
			break;
		case PTP_VENDOR_MICROSOFT:
		case PTP_VENDOR_MTP:
			for (i=0;i<ARRAYSIZE(ptp_ofc_mtp_trans);i++)
				if (ofc == ptp_ofc_mtp_trans[i].ofc)
					return snprintf(txt, spaceleft, "%s", _(ptp_ofc_mtp_trans[i].format));
			break;
		default:break;
		}
	}
	return snprintf (txt, spaceleft,_("Unknown(%04x)"), ofc);
}

typedef struct {
	uint16_t opcode;
	const char *name;
} ptp_opcode_trans_t;

ptp_opcode_trans_t ptp_opcode_trans[] = {
	{PTP_OC_Undefined,N_("Undefined")},
	{PTP_OC_GetDeviceInfo,N_("Get device info")},
	{PTP_OC_OpenSession,N_("Open session")},
	{PTP_OC_CloseSession,N_("Close session")},
	{PTP_OC_GetStorageIDs,N_("Get storage IDs")},
	{PTP_OC_GetStorageInfo,N_("Get storage info")},
	{PTP_OC_GetNumObjects,N_("Get number of objects")},
	{PTP_OC_GetObjectHandles,N_("Get object handles")},
	{PTP_OC_GetObjectInfo,N_("Get object info")},
	{PTP_OC_GetObject,N_("Get object")},
	{PTP_OC_GetThumb,N_("Get thumbnail")},
	{PTP_OC_DeleteObject,N_("Delete object")},
	{PTP_OC_SendObjectInfo,N_("Send object info")},
	{PTP_OC_SendObject,N_("Send object")},
	{PTP_OC_InitiateCapture,N_("Initiate capture")},
	{PTP_OC_FormatStore,N_("Format storage")},
	{PTP_OC_ResetDevice,N_("Reset device")},
	{PTP_OC_SelfTest,N_("Self test device")},
	{PTP_OC_SetObjectProtection,N_("Set object protection")},
	{PTP_OC_PowerDown,N_("Power down device")},
	{PTP_OC_GetDevicePropDesc,N_("Get device property description")},
	{PTP_OC_GetDevicePropValue,N_("Get device property value")},
	{PTP_OC_SetDevicePropValue,N_("Set device property value")},
	{PTP_OC_ResetDevicePropValue,N_("Reset device property value")},
	{PTP_OC_TerminateOpenCapture,N_("Terminate open capture")},
	{PTP_OC_MoveObject,N_("Move object")},
	{PTP_OC_CopyObject,N_("Copy object")},
	{PTP_OC_GetPartialObject,N_("Get partial object")},
	{PTP_OC_InitiateOpenCapture,N_("Initiate open capture")},
	/* PTP v1.1 operation codes */
	{PTP_OC_StartEnumHandles,N_("Start Enumerate Handles")},
	{PTP_OC_EnumHandles,N_("Enumerate Handles")},
	{PTP_OC_StopEnumHandles,N_("Stop Enumerate Handles")},
	{PTP_OC_GetVendorExtensionMaps,N_("Get Vendor Extension Maps")},
	{PTP_OC_GetVendorDeviceInfo,N_("Get Vendor Device Info")},
	{PTP_OC_GetResizedImageObject,N_("Get Resized Image Object")},
	{PTP_OC_GetFilesystemManifest,N_("Get Filesystem Manifest")},
	{PTP_OC_GetStreamInfo,N_("Get Stream Info")},
	{PTP_OC_GetStream,N_("Get Stream")},
};

ptp_opcode_trans_t ptp_opcode_mtp_trans[] = {
	{PTP_OC_MTP_GetObjectPropsSupported,N_("Get object properties supported")},
	{PTP_OC_MTP_GetObjectPropDesc,N_("Get object property description")},
	{PTP_OC_MTP_GetObjectPropValue,N_("Get object property value")},
	{PTP_OC_MTP_SetObjectPropValue,N_("Set object property value")},
	{PTP_OC_MTP_GetObjPropList,N_("Get object property list")},
	{PTP_OC_MTP_SetObjPropList,N_("Set object property list")},
	{PTP_OC_MTP_GetInterdependendPropdesc,N_("Get interdependent property description")},
	{PTP_OC_MTP_SendObjectPropList,N_("Send object property list")},
	{PTP_OC_MTP_GetObjectReferences,N_("Get object references")},
	{PTP_OC_MTP_SetObjectReferences,N_("Set object references")},
	{PTP_OC_MTP_UpdateDeviceFirmware,N_("Update device firmware")},
	{PTP_OC_MTP_Skip,N_("Skip to next position in playlist")},

	/* WMDRMPD Extensions */
	{PTP_OC_MTP_WMDRMPD_GetSecureTimeChallenge,N_("Get secure time challenge")},
	{PTP_OC_MTP_WMDRMPD_GetSecureTimeResponse,N_("Get secure time response")},
	{PTP_OC_MTP_WMDRMPD_SetLicenseResponse,N_("Set license response")},
	{PTP_OC_MTP_WMDRMPD_GetSyncList,N_("Get sync list")},
	{PTP_OC_MTP_WMDRMPD_SendMeterChallengeQuery,N_("Send meter challenge query")},
	{PTP_OC_MTP_WMDRMPD_GetMeterChallenge,N_("Get meter challenge")},
	{PTP_OC_MTP_WMDRMPD_SetMeterResponse,N_("Get meter response")},
	{PTP_OC_MTP_WMDRMPD_CleanDataStore,N_("Clean data store")},
	{PTP_OC_MTP_WMDRMPD_GetLicenseState,N_("Get license state")},
	{PTP_OC_MTP_WMDRMPD_SendWMDRMPDCommand,N_("Send WMDRM-PD Command")},
	{PTP_OC_MTP_WMDRMPD_SendWMDRMPDRequest,N_("Send WMDRM-PD Request")},

	/* WMPPD Extensions */
	{PTP_OC_MTP_WMPPD_ReportAddedDeletedItems,N_("Report Added/Deleted Items")},
	{PTP_OC_MTP_WMPPD_ReportAcquiredItems,N_("Report Acquired Items")},
	{PTP_OC_MTP_WMPPD_PlaylistObjectPref,N_("Get transferable playlist types")},

	/* WMDRMPD Extensions... these have no identifiers associated with them */
	{PTP_OC_MTP_WMDRMPD_SendWMDRMPDAppRequest,N_("Send WMDRM-PD Application Request")},
	{PTP_OC_MTP_WMDRMPD_GetWMDRMPDAppResponse,N_("Get WMDRM-PD Application Response")},
	{PTP_OC_MTP_WMDRMPD_EnableTrustedFilesOperations,N_("Enable trusted file operations")},
	{PTP_OC_MTP_WMDRMPD_DisableTrustedFilesOperations,N_("Disable trusted file operations")},
	{PTP_OC_MTP_WMDRMPD_EndTrustedAppSession,N_("End trusted application session")},

	/* AAVT Extensions */
	{PTP_OC_MTP_AAVT_OpenMediaSession,N_("Open Media Session")},
	{PTP_OC_MTP_AAVT_CloseMediaSession,N_("Close Media Session")},
	{PTP_OC_MTP_AAVT_GetNextDataBlock,N_("Get Next Data Block")},
	{PTP_OC_MTP_AAVT_SetCurrentTimePosition,N_("Set Current Time Position")},

	/* WMDRMND Extensions */
	{PTP_OC_MTP_WMDRMND_SendRegistrationRequest,N_("Send Registration Request")},
	{PTP_OC_MTP_WMDRMND_GetRegistrationResponse,N_("Get Registration Response")},
	{PTP_OC_MTP_WMDRMND_GetProximityChallenge,N_("Get Proximity Challenge")},
	{PTP_OC_MTP_WMDRMND_SendProximityResponse,N_("Send Proximity Response")},
	{PTP_OC_MTP_WMDRMND_SendWMDRMNDLicenseRequest,N_("Send WMDRM-ND License Request")},
	{PTP_OC_MTP_WMDRMND_GetWMDRMNDLicenseResponse,N_("Get WMDRM-ND License Response")},

	/* WiFi Provisioning MTP Extension Codes (microsoft.com/WPDWCN: 1.0) */
	{PTP_OC_MTP_WPDWCN_ProcessWFCObject,N_("Process WFC Object")},

	/* Android Direct I/O Extensions */
	{PTP_OC_ANDROID_GetPartialObject64,N_("Get Partial Object (64bit Offset)")},
	{PTP_OC_ANDROID_SendPartialObject,N_("Send Partial Object")},
	{PTP_OC_ANDROID_TruncateObject,N_("Truncate Object")},
	{PTP_OC_ANDROID_BeginEditObject,N_("Begin Edit Object")},
	{PTP_OC_ANDROID_EndEditObject,N_("End Edit Object")},
};

ptp_opcode_trans_t ptp_opcode_nikon_trans[] = {
	{PTP_OC_NIKON_GetProfileAllData,"GetProfileAllData"},
	{PTP_OC_NIKON_SendProfileData,"SendProfileData"},
	{PTP_OC_NIKON_DeleteProfile,"DeleteProfile"},
	{PTP_OC_NIKON_SetProfileData,"SetProfileData"},
	{PTP_OC_NIKON_AdvancedTransfer,"AdvancedTransfer"},
	{PTP_OC_NIKON_GetFileInfoInBlock,"GetFileInfoInBlock"},
	{PTP_OC_NIKON_InitiateCaptureRecInSdram,"InitiateCaptureRecInSdram"},
	{PTP_OC_NIKON_AfDrive,"AfDrive"},
	{PTP_OC_NIKON_ChangeCameraMode,"ChangeCameraMode"},
	{PTP_OC_NIKON_DelImageSDRAM,"DelImageSDRAM"},
	{PTP_OC_NIKON_GetLargeThumb,"GetLargeThumb"},
	{PTP_OC_NIKON_CurveDownload,"CurveDownload"},
	{PTP_OC_NIKON_CurveUpload,"CurveUpload"},
	{PTP_OC_NIKON_GetEvent,"GetEvent"},
	{PTP_OC_NIKON_DeviceReady,"DeviceReady"},
	{PTP_OC_NIKON_SetPreWBData,"SetPreWBData"},
	{PTP_OC_NIKON_GetVendorPropCodes,"GetVendorPropCodes"},
	{PTP_OC_NIKON_AfCaptureSDRAM,"AfCaptureSDRAM"},
	{PTP_OC_NIKON_GetPictCtrlData,"GetPictCtrlData"},
	{PTP_OC_NIKON_SetPictCtrlData,"SetPictCtrlData"},
	{PTP_OC_NIKON_DelCstPicCtrl,"DelCstPicCtrl"},
	{PTP_OC_NIKON_GetPicCtrlCapability,"GetPicCtrlCapability"},
	{PTP_OC_NIKON_GetPreviewImg,"GetPreviewImg"},
	{PTP_OC_NIKON_StartLiveView,"StartLiveView"},
	{PTP_OC_NIKON_EndLiveView,"EndLiveView"},
	{PTP_OC_NIKON_GetLiveViewImg,"GetLiveViewImg"},
	{PTP_OC_NIKON_MfDrive,"MfDrive"},
	{PTP_OC_NIKON_ChangeAfArea,"ChangeAfArea"},
	{PTP_OC_NIKON_AfDriveCancel,"AfDriveCancel"},
	{PTP_OC_NIKON_InitiateCaptureRecInMedia,"InitiateCaptureRecInMedia"},
	{PTP_OC_NIKON_GetVendorStorageIDs,"GetVendorStorageIDs"},
	{PTP_OC_NIKON_StartMovieRecInCard,"StartMovieRecInCard"},
	{PTP_OC_NIKON_EndMovieRec,"EndMovieRec"},
	{PTP_OC_NIKON_TerminateCapture,"TerminateCapture"},
	{PTP_OC_NIKON_GetDevicePTPIPInfo,"GetDevicePTPIPInfo"},
	{PTP_OC_NIKON_GetPartialObjectHiSpeed,"GetPartialObjectHiSpeed"},
	{PTP_OC_NIKON_GetDevicePropEx,"GetDevicePropEx"},
	{PTP_OC_NIKON_GetFhdPicture,"GetFhdPicture"},
	{PTP_OC_NIKON_StartSpotWb,"StartSpotWb"},
	{PTP_OC_NIKON_EndSpotWb,"EndSpotWb"},
	{PTP_OC_NIKON_ChangeSpotWbArea,"ChangeSpotWbArea"},
	{PTP_OC_NIKON_MeasureSpotWb,"MeasureSpotWb"},
	{PTP_OC_NIKON_EndSpotWbResultDisp,"EndSpotWbResultDisp"},
	{PTP_OC_NIKON_CancelImagesInSDRAM,"CancelImagesInSDRAM"},
	{PTP_OC_NIKON_GetSBHandles,"GetSBHandles"},
	{PTP_OC_NIKON_GetSBAttrDesc,"GetSBAttrDesc"},
	{PTP_OC_NIKON_GetSBAttrValue,"GetSBAttrValue"},
	{PTP_OC_NIKON_SetSBAttrValue,"SetSBAttrValue"},
	{PTP_OC_NIKON_GetSBGroupAttrDesc,"GetSBGroupAttrDesc"},
	{PTP_OC_NIKON_GetSBGroupAttrValue,"GetSBGroupAttrValue"},
	{PTP_OC_NIKON_SetSBGroupAttrValue,"SetSBGroupAttrValue"},
	{PTP_OC_NIKON_TestFlash,"TestFlash"},
	{PTP_OC_NIKON_GetEventEx,"GetEventEx"},
	{PTP_OC_NIKON_MirrorUpCancel,"MirrorUpCancel"},
	{PTP_OC_NIKON_SaveCameraSetting,"SaveCameraSetting"},
	{PTP_OC_NIKON_GetObjectSize,"GetObjectSize"},
	{PTP_OC_NIKON_GetLiveViewCompressedSize,"GetLiveViewCompressedSize"},
	{PTP_OC_NIKON_StartTracking,"StartTracking"},
	{PTP_OC_NIKON_EndTracking,"EndTracking"},
	{PTP_OC_NIKON_ChangeAELock,"ChangeAELock"},
	{PTP_OC_NIKON_GetLiveViewImageEx,"GetLiveViewImageEx"},
	{PTP_OC_NIKON_GetPartialObjectEx,"GetPartialObjectEx"},
	{PTP_OC_NIKON_GetManualSettingLensData,"GetManualSettingLensData"},
	{PTP_OC_NIKON_PowerZoomByFocalLength,"PowerZoomByFocalLength"},
	{PTP_OC_NIKON_ActiveSelectionControl,"ActiveSelectionControl"},
	{PTP_OC_NIKON_ChangeMonitorOff,"ChangeMonitorOff"},
	{PTP_OC_NIKON_InitiatePixelMapping,"InitiatePixelMapping"},
	{PTP_OC_NIKON_GetObjectsMetaData,"GetObjectsMetaData"},
	{PTP_OC_NIKON_ChangeApplicationMode,"ChangeApplicationMode"},
	{PTP_OC_NIKON_ResetMenu,"ResetMenu"},
};

ptp_opcode_trans_t ptp_opcode_canon_trans[] = {
	{PTP_OC_CANON_GetPartialObjectInfo,"GetPartialObjectInfo"},
	{PTP_OC_CANON_SetObjectArchive,"SetObjectArchive"},
	{PTP_OC_CANON_KeepDeviceOn,"KeepDeviceOn"},
	{PTP_OC_CANON_LockDeviceUI,"LockDeviceUI"},
	{PTP_OC_CANON_UnlockDeviceUI,"UnlockDeviceUI"},
	{PTP_OC_CANON_GetObjectHandleByName,"GetObjectHandleByName"},
	{PTP_OC_CANON_InitiateReleaseControl,"InitiateReleaseControl"},
	{PTP_OC_CANON_TerminateReleaseControl,"TerminateReleaseControl"},
	{PTP_OC_CANON_TerminatePlaybackMode,"TerminatePlaybackMode"},
	{PTP_OC_CANON_ViewfinderOn,"ViewfinderOn"},
	{PTP_OC_CANON_ViewfinderOff,"ViewfinderOff"},
	{PTP_OC_CANON_DoAeAfAwb,"DoAeAfAwb"},
	{PTP_OC_CANON_GetCustomizeSpec,"GetCustomizeSpec"},
	{PTP_OC_CANON_GetCustomizeItemInfo,"GetCustomizeItemInfo"},
	{PTP_OC_CANON_GetCustomizeData,"GetCustomizeData"},
	{PTP_OC_CANON_SetCustomizeData,"SetCustomizeData"},
	{PTP_OC_CANON_GetCaptureStatus,"GetCaptureStatus"},
	{PTP_OC_CANON_CheckEvent,"CheckEvent"},
	{PTP_OC_CANON_FocusLock,"FocusLock"},
	{PTP_OC_CANON_FocusUnlock,"FocusUnlock"},
	{PTP_OC_CANON_GetLocalReleaseParam,"GetLocalReleaseParam"},
	{PTP_OC_CANON_SetLocalReleaseParam,"SetLocalReleaseParam"},
	{PTP_OC_CANON_AskAboutPcEvf,"AskAboutPcEvf"},
	{PTP_OC_CANON_SendPartialObject,"SendPartialObject"},
	{PTP_OC_CANON_InitiateCaptureInMemory,"InitiateCaptureInMemory"},
	{PTP_OC_CANON_GetPartialObjectEx,"GetPartialObjectEx"},
	{PTP_OC_CANON_SetObjectTime,"SetObjectTime"},
	{PTP_OC_CANON_GetViewfinderImage,"GetViewfinderImage"},
	{PTP_OC_CANON_GetObjectAttributes,"GetObjectAttributes"},
	{PTP_OC_CANON_ChangeUSBProtocol,"ChangeUSBProtocol"},
	{PTP_OC_CANON_GetChanges,"GetChanges"},
	{PTP_OC_CANON_GetObjectInfoEx,"GetObjectInfoEx"},
	{PTP_OC_CANON_InitiateDirectTransfer,"InitiateDirectTransfer"},
	{PTP_OC_CANON_TerminateDirectTransfer ,"TerminateDirectTransfer "},
	{PTP_OC_CANON_SendObjectInfoByPath ,"SendObjectInfoByPath "},
	{PTP_OC_CANON_SendObjectByPath ,"SendObjectByPath "},
	{PTP_OC_CANON_InitiateDirectTansferEx,"InitiateDirectTansferEx"},
	{PTP_OC_CANON_GetAncillaryObjectHandles,"GetAncillaryObjectHandles"},
	{PTP_OC_CANON_GetTreeInfo ,"GetTreeInfo "},
	{PTP_OC_CANON_GetTreeSize ,"GetTreeSize "},
	{PTP_OC_CANON_NotifyProgress ,"NotifyProgress "},
	{PTP_OC_CANON_NotifyCancelAccepted,"NotifyCancelAccepted"},
	{PTP_OC_CANON_GetDirectory,"GetDirectory"},
	{PTP_OC_CANON_SetPairingInfo,"SetPairingInfo"},
	{PTP_OC_CANON_GetPairingInfo,"GetPairingInfo"},
	{PTP_OC_CANON_DeletePairingInfo,"DeletePairingInfo"},
	{PTP_OC_CANON_GetMACAddress,"GetMACAddress"},
	{PTP_OC_CANON_SetDisplayMonitor,"SetDisplayMonitor"},
	{PTP_OC_CANON_PairingComplete,"PairingComplete"},
	{PTP_OC_CANON_GetWirelessMAXChannel,"GetWirelessMAXChannel"},
	{PTP_OC_CANON_GetWebServiceSpec,"GetWebServiceSpec"},
	{PTP_OC_CANON_GetWebServiceData,"GetWebServiceData"},
	{PTP_OC_CANON_SetWebServiceData,"SetWebServiceData"},
	{PTP_OC_CANON_GetRootCertificateSpec,"GetRootCertificateSpec"},
	{PTP_OC_CANON_GetRootCertificateData,"GetRootCertificateData"},
	{PTP_OC_CANON_SetRootCertificateData,"SetRootCertificateData"},
	{PTP_OC_CANON_DeleteRootCertificateData,"DeleteRootCertificateData"},
	{PTP_OC_CANON_EOS_GetStorageIDs,"EOS_GetStorageIDs"},
	{PTP_OC_CANON_EOS_GetStorageInfo,"EOS_GetStorageInfo"},
	{PTP_OC_CANON_EOS_GetObjectInfo,"EOS_GetObjectInfo"},
	{PTP_OC_CANON_EOS_GetObject,"EOS_GetObject"},
	{PTP_OC_CANON_EOS_DeleteObject,"EOS_DeleteObject"},
	{PTP_OC_CANON_EOS_FormatStore,"EOS_FormatStore"},
	{PTP_OC_CANON_EOS_GetPartialObject,"EOS_GetPartialObject"},
	{PTP_OC_CANON_EOS_GetDeviceInfoEx,"EOS_GetDeviceInfoEx"},
	{PTP_OC_CANON_EOS_GetObjectInfoEx,"EOS_GetObjectInfoEx"},
	{PTP_OC_CANON_EOS_GetThumbEx,"EOS_GetThumbEx"},
	{PTP_OC_CANON_EOS_SendPartialObject,"EOS_SendPartialObject"},
	{PTP_OC_CANON_EOS_SetObjectAttributes,"EOS_SetObjectAttributes"},
	{PTP_OC_CANON_EOS_GetObjectTime,"EOS_GetObjectTime"},
	{PTP_OC_CANON_EOS_SetObjectTime,"EOS_SetObjectTime"},
	{PTP_OC_CANON_EOS_RemoteRelease,"EOS_RemoteRelease"},
	{PTP_OC_CANON_EOS_SetDevicePropValueEx,"EOS_SetDevicePropValueEx"},
	{PTP_OC_CANON_EOS_GetRemoteMode,"EOS_GetRemoteMode"},
	{PTP_OC_CANON_EOS_SetRemoteMode,"EOS_SetRemoteMode"},
	{PTP_OC_CANON_EOS_SetEventMode,"EOS_SetEventMode"},
	{PTP_OC_CANON_EOS_GetEvent,"EOS_GetEvent"},
	{PTP_OC_CANON_EOS_TransferComplete,"EOS_TransferComplete"},
	{PTP_OC_CANON_EOS_CancelTransfer,"EOS_CancelTransfer"},
	{PTP_OC_CANON_EOS_ResetTransfer,"EOS_ResetTransfer"},
	{PTP_OC_CANON_EOS_PCHDDCapacity,"EOS_PCHDDCapacity"},
	{PTP_OC_CANON_EOS_SetUILock,"EOS_SetUILock"},
	{PTP_OC_CANON_EOS_ResetUILock,"EOS_ResetUILock"},
	{PTP_OC_CANON_EOS_KeepDeviceOn,"EOS_KeepDeviceOn"},
	{PTP_OC_CANON_EOS_SetNullPacketMode,"EOS_SetNullPacketMode"},
	{PTP_OC_CANON_EOS_UpdateFirmware,"EOS_UpdateFirmware"},
	{PTP_OC_CANON_EOS_TransferCompleteDT,"EOS_TransferCompleteDT"},
	{PTP_OC_CANON_EOS_CancelTransferDT,"EOS_CancelTransferDT"},
	{PTP_OC_CANON_EOS_SetWftProfile,"EOS_SetWftProfile"},
	{PTP_OC_CANON_EOS_GetWftProfile,"EOS_GetWftProfile"},
	{PTP_OC_CANON_EOS_SetProfileToWft,"EOS_SetProfileToWft"},
	{PTP_OC_CANON_EOS_BulbStart,"EOS_BulbStart"},
	{PTP_OC_CANON_EOS_BulbEnd,"EOS_BulbEnd"},
	{PTP_OC_CANON_EOS_RequestDevicePropValue,"EOS_RequestDevicePropValue"},
	{PTP_OC_CANON_EOS_RemoteReleaseOn,"EOS_RemoteReleaseOn"},
	{PTP_OC_CANON_EOS_RemoteReleaseOff,"EOS_RemoteReleaseOff"},
	{PTP_OC_CANON_EOS_RegistBackgroundImage,"EOS_RegistBackgroundImage"},
	{PTP_OC_CANON_EOS_ChangePhotoStudioMode,"EOS_ChangePhotoStudioMode"},
	{PTP_OC_CANON_EOS_GetPartialObjectEx,"EOS_GetPartialObjectEx"},
	{PTP_OC_CANON_EOS_ResetMirrorLockupState,"EOS_ResetMirrorLockupState"},
	{PTP_OC_CANON_EOS_PopupBuiltinFlash,"EOS_PopupBuiltinFlash"},
	{PTP_OC_CANON_EOS_EndGetPartialObjectEx,"EOS_EndGetPartialObjectEx"},
	{PTP_OC_CANON_EOS_MovieSelectSWOn,"EOS_MovieSelectSWOn"},
	{PTP_OC_CANON_EOS_MovieSelectSWOff,"EOS_MovieSelectSWOff"},
	{PTP_OC_CANON_EOS_GetCTGInfo,"EOS_GetCTGInfo"},
	{PTP_OC_CANON_EOS_GetLensAdjust,"EOS_GetLensAdjust"},
	{PTP_OC_CANON_EOS_SetLensAdjust,"EOS_SetLensAdjust"},
	{PTP_OC_CANON_EOS_ReadyToSendMusic,"EOS_ReadyToSendMusic"},
	{PTP_OC_CANON_EOS_CreateHandle,"EOS_CreateHandle"},
	{PTP_OC_CANON_EOS_SendPartialObjectEx,"EOS_SendPartialObjectEx"},
	{PTP_OC_CANON_EOS_EndSendPartialObjectEx,"EOS_EndSendPartialObjectEx"},
	{PTP_OC_CANON_EOS_SetCTGInfo,"EOS_SetCTGInfo"},
	{PTP_OC_CANON_EOS_SetRequestOLCInfoGroup,"EOS_SetRequestOLCInfoGroup"},
	{PTP_OC_CANON_EOS_SetRequestRollingPitchingLevel,"EOS_SetRequestRollingPitchingLevel"},
	{PTP_OC_CANON_EOS_GetCameraSupport,"EOS_GetCameraSupport"},
	{PTP_OC_CANON_EOS_SetRating,"EOS_SetRating"},
	{PTP_OC_CANON_EOS_RequestInnerDevelopStart,"EOS_RequestInnerDevelopStart"},
	{PTP_OC_CANON_EOS_RequestInnerDevelopParamChange,"EOS_RequestInnerDevelopParamChange"},
	{PTP_OC_CANON_EOS_RequestInnerDevelopEnd,"EOS_RequestInnerDevelopEnd"},
	{PTP_OC_CANON_EOS_GpsLoggingDataMode,"EOS_GpsLoggingDataMode"},
	{PTP_OC_CANON_EOS_GetGpsLogCurrentHandle,"EOS_GetGpsLogCurrentHandle"},
	{PTP_OC_CANON_EOS_InitiateViewfinder,"EOS_InitiateViewfinder"},
	{PTP_OC_CANON_EOS_TerminateViewfinder,"EOS_TerminateViewfinder"},
	{PTP_OC_CANON_EOS_GetViewFinderData,"EOS_GetViewFinderData"},
	{PTP_OC_CANON_EOS_DoAf,"EOS_DoAf"},
	{PTP_OC_CANON_EOS_DriveLens,"EOS_DriveLens"},
	{PTP_OC_CANON_EOS_DepthOfFieldPreview,"EOS_DepthOfFieldPreview"},
	{PTP_OC_CANON_EOS_ClickWB,"EOS_ClickWB"},
	{PTP_OC_CANON_EOS_Zoom,"EOS_Zoom"},
	{PTP_OC_CANON_EOS_ZoomPosition,"EOS_ZoomPosition"},
	{PTP_OC_CANON_EOS_SetLiveAfFrame,"EOS_SetLiveAfFrame"},
	{PTP_OC_CANON_EOS_TouchAfPosition,"EOS_TouchAfPosition"},
	{PTP_OC_CANON_EOS_SetLvPcFlavoreditMode,"EOS_SetLvPcFlavoreditMode"},
	{PTP_OC_CANON_EOS_SetLvPcFlavoreditParam,"EOS_SetLvPcFlavoreditParam"},
	{PTP_OC_CANON_EOS_RequestSensorCleaning,"EOS_RequestSensorCleaning"},
	{PTP_OC_CANON_EOS_AfCancel,"EOS_AfCancel"},
	{PTP_OC_CANON_EOS_SetDefaultCameraSetting,"EOS_SetDefaultCameraSetting"},
	{PTP_OC_CANON_EOS_GetAEData,"EOS_GetAEData"},
	{PTP_OC_CANON_EOS_NotifyNetworkError,"EOS_NotifyNetworkError"},
	{PTP_OC_CANON_EOS_AdapterTransferProgress,"EOS_AdapterTransferProgress"},
	{PTP_OC_CANON_EOS_TransferCompleteFTP,"EOS_TransferCompleteFTP"},
	{PTP_OC_CANON_EOS_CancelTransferFTP,"EOS_CancelTransferFTP"},
	{PTP_OC_CANON_EOS_FAPIMessageTX,"EOS_FAPIMessageTX"},
	{PTP_OC_CANON_EOS_FAPIMessageRX,"EOS_FAPIMessageRX"},
	{PTP_OC_CANON_EOS_SetImageRecoveryData,"EOS_SetImageRecoveryData"},
	{PTP_OC_CANON_EOS_GetImageRecoveryList,"EOS_GetImageRecoveryList"},
	{PTP_OC_CANON_EOS_FormatImageRecoveryData,"EOS_FormatImageRecoveryData"},
	{PTP_OC_CANON_EOS_GetPresetLensAdjustParam,"EOS_GetPresetLensAdjustParam"},
	{PTP_OC_CANON_EOS_GetRawDispImage,"EOS_GetRawDispImage"},
	{PTP_OC_CANON_EOS_SaveImageRecoveryData,"EOS_SaveImageRecoveryData"},
	{PTP_OC_CANON_EOS_RequestBLE,"EOS_RequestBLE"},
	{PTP_OC_CANON_EOS_DrivePowerZoom,"EOS_DrivePowerZoom"},
	{PTP_OC_CANON_EOS_GetIptcData,"EOS_GetIptcData"},
	{PTP_OC_CANON_EOS_SetIptcData,"EOS_SetIptcData"},
	{PTP_OC_CANON_EOS_GetObjectInfo64,"EOS_GetObjectInfo64"},
	{PTP_OC_CANON_EOS_GetObject64,"EOS_GetObject64"},
	{PTP_OC_CANON_EOS_GetPartialObject64,"EOS_GetPartialObject64"},
	{PTP_OC_CANON_EOS_GetObjectInfoEx64,"EOS_GetObjectInfoEx64"},
	{PTP_OC_CANON_EOS_GetPartialObjectEX64,"EOS_GetPartialObjectEX64"},
	{PTP_OC_CANON_EOS_CreateHandle64,"EOS_CreateHandle64"},
	{PTP_OC_CANON_EOS_NotifyEstimateNumberofImport,"EOS_NotifyEstimateNumberofImport"},
	{PTP_OC_CANON_EOS_NotifyNumberofImported,"EOS_NotifyNumberofImported"},
	{PTP_OC_CANON_EOS_NotifySizeOfPartialDataTransfer,"EOS_NotifySizeOfPartialDataTransfer"},
	{PTP_OC_CANON_EOS_NotifyFinish,"EOS_NotifyFinish"},
	{PTP_OC_CANON_EOS_SetImageRecoveryDataEx,"EOS_SetImageRecoveryDataEx"},
	{PTP_OC_CANON_EOS_GetImageRecoveryListEx,"EOS_GetImageRecoveryListEx"},
	{PTP_OC_CANON_EOS_NotifyAutoTransferStatus,"EOS_NotifyAutoTransferStatus"},
	{PTP_OC_CANON_EOS_GetReducedObject,"EOS_GetReducedObject"},
	{PTP_OC_CANON_EOS_NotifySaveComplete,"EOS_NotifySaveComplete"},
	{PTP_OC_CANON_EOS_GetObjectURL,"EOS_GetObjectURL"},
	{PTP_OC_CANON_SetRemoteShootingMode,"SetRemoteShootingMode"},
	{PTP_OC_CANON_SetModeDialDisable,"SetModeDialDisable"},
	{PTP_OC_CANON_EOS_SetFELock,"EOS_SetFELock"},
	{PTP_OC_CANON_DeleteWebServiceData,"DeleteWebServiceData"},
	{PTP_OC_CANON_GetGpsMobilelinkObjectInfo,"GetGpsMobilelinkObjectInfo"},
	{PTP_OC_CANON_SendGpsTagInfo,"SendGpsTagInfo"},
	{PTP_OC_CANON_GetTranscodeApproxSize,"GetTranscodeApproxSize"},
	{PTP_OC_CANON_RequestTranscodeStart,"RequestTranscodeStart"},
	{PTP_OC_CANON_RequestTranscodeCancel,"RequestTranscodeCancel"},
	{PTP_OC_CANON_EOS_SendHostInfo,"EOS_SendHostInfo"},
	{PTP_OC_CANON_EOS_NotifyBtStatus,"EOS_NotifyBtStatus"},
	{PTP_OC_CANON_EOS_SetAdapterBatteryReport,"EOS_SetAdapterBatteryReport"},
	{PTP_OC_CANON_EOS_CompleteAutoSendImages,"EOS_CompleteAutoSendImages"},
	{PTP_OC_CANON_EOS_GetTranscodedBlock,"EOS_GetTranscodedBlock"},
	{PTP_OC_CANON_EOS_TransferCompleteTranscodedBlock,"EOS_TransferCompleteTranscodedBlock"},
	{PTP_OC_CANON_EOS_GetWFTData,"EOS_GetWFTData"},
	{PTP_OC_CANON_EOS_SetWFTData,"EOS_SetWFTData"},
	{PTP_OC_CANON_EOS_ChangeWFTSettingNumber,"EOS_ChangeWFTSettingNumber"},
	{PTP_OC_CANON_EOS_GetPictureStylePCFlavorParam,"EOS_GetPictureStylePCFlavorParam"},
	{PTP_OC_CANON_EOS_SetPictureStylePCFlavorParam,"EOS_SetPictureStylePCFlavorParam"},
	{PTP_OC_CANON_EOS_SetCAssistMode,"EOS_SetCAssistMode"},
	{PTP_OC_CANON_EOS_GetCAssistPresetThumb,"EOS_GetCAssistPresetThumb"},
	{PTP_OC_CANON_EOS_DeleteWFTSettingNumber,"EOS_DeleteWFTSettingNumber"},
	{PTP_OC_CANON_GetDebugLog,"CANON_GetDebugLog"},
	{PTP_OC_CANON_Clear_CCAPI_UsageCount,"CANON_Clear_CCAPI_UsageCount"},
	{PTP_OC_CANON_SetMetaProperty,"CANON_SetMetaProperty"},
	{PTP_OC_CANON_EOS_SetManipulationToggleImageSide,"CANON_EOS_SetManipulationToggleImageSide"},
	{PTP_OC_CANON_EOS_GetLensSerialNumHistory,"CANON_EOS_GetLensSerialNumHistory"},
	{PTP_OC_CANON_EOS_SetRemoteErrLogMode,"CANON_EOS_SetRemoteErrLogMode"},
	{PTP_OC_CANON_EOS_GetRemoteErrLogMode,"CANON_EOS_GetRemoteErrLogMode"},
	{PTP_OC_CANON_EOS_GetDebugLogEx,"CANON_EOS_GetDebugLogEx"},
	{PTP_OC_CANON_EOS_SendWifiSetting,"CANON_EOS_SendWifiSetting"},
	{PTP_OC_CANON_EOS_SendWifiDetailSetting,"CANON_EOS_SendWifiDetailSetting"},
	{PTP_OC_CANON_EOS_EdgeFocusPosition,"CANON_EOS_EdgeFocusPosition"},
	{PTP_OC_CANON_EOS_DriveFocusToEdge,"CANON_EOS_DriveFocusToEdge"},
	{PTP_OC_CANON_EOS_DriveFocus,"CANON_EOS_DriveFocus"},
	{PTP_OC_CANON_EOS_DV_Get_WavInfo,"CANON_EOS_DV_Get_WavInfo"},
	{PTP_OC_CANON_EOS_DV_Overwrite_Contents_XML,"CANON_EOS_DV_Overwrite_Contents_XML"},
	{PTP_OC_CANON_EOS_DV_Get_Template_XML,"CANON_EOS_DV_Get_Template_XML"},
};

ptp_opcode_trans_t ptp_opcode_sony_trans[] = {
	{PTP_OC_SONY_SDIO_Connect,"SDIO_Connect"},
	{PTP_OC_SONY_SDIO_GetExtDeviceInfo,"SDIO_GetExtDeviceInfo"},
	{PTP_OC_SONY_GetDevicePropdesc,"GetDevicePropdesc"},
	{PTP_OC_SONY_GetDevicePropertyValue,"GetDevicePropertyValue"},
	{PTP_OC_SONY_SDIO_SetExtDevicePropValue,"SDIO_SetExtDevicePropValue"},
	{PTP_OC_SONY_GetControlDeviceDesc,"GetControlDeviceDesc"},
	{PTP_OC_SONY_SDIO_ControlDevice,"SDIO_ControlDevice"},
	{PTP_OC_SONY_SDIO_GetAllExtDevicePropInfo,"SDIO_GetAllExtDevicePropInfo"},
	{PTP_OC_SONY_SDIO_OpenSession,"SDIO_OpenSession"},
	{PTP_OC_SONY_SDIO_GetPartialLargeObject,"SDIO_GetPartialLargeObject"},
	{PTP_OC_SONY_SDIO_SetContentsTransferMode,"SDIO_SetContentsTransferMode"},
	{PTP_OC_SONY_SDIO_GetDisplayStringList,"SDIO_GetDisplayStringList"},
	{PTP_OC_SONY_SDIO_GetLensInformation,"SDIO_GetLensInformation"},
	{PTP_OC_SONY_SDIO_OperationsResultsSupported,"SDIO_OperationsResultsSupported"},
	{PTP_OC_SONY_QX_GetAllDevicePropData,"QX_GetAllDevicePropData"},
	{PTP_OC_SONY_QX_SetControlDeviceB,"QX_SetControlDeviceB"},
	{PTP_OC_SONY_QX_SetControlDeviceA,"QX_SetControlDeviceA"},
	{PTP_OC_SONY_QX_GetSDIOGetExtDeviceInfo,"QX_GetSDIOGetExtDeviceInfo"},
	{PTP_OC_SONY_QX_Connect,"QX_Connect"},
	{PTP_OC_SONY_QX_SetExtPictureProfile, "QX_SetExtPictureProfile"},
	{PTP_OC_SONY_QX_GetExtPictureProfile, "QX_GetExtPictureProfile"},
	{PTP_OC_SONY_QX_GetExtLensInfo, "QX_GetExtLensInfo"},
	{PTP_OC_SONY_QX_SendUpdateFile, "QX_SendUpdateFile"},
};

ptp_opcode_trans_t ptp_opcode_parrot_trans[] = {
	{PTP_OC_PARROT_GetSunshineValues,"GetSunshineValues"},
	{PTP_OC_PARROT_GetTemperatureValues,"GetTemperatureValues"},
	{PTP_OC_PARROT_GetAngleValues,"GetAngleValues"},
	{PTP_OC_PARROT_GetGpsValues,"GetGpsValues"},
	{PTP_OC_PARROT_GetGyroscopeValues,"GetGyroscopeValues"},
	{PTP_OC_PARROT_GetAccelerometerValues,"GetAccelerometerValues"},
	{PTP_OC_PARROT_GetMagnetometerValues,"GetMagnetometerValues"},
	{PTP_OC_PARROT_GetImuValues,"GetImuValues"},
	{PTP_OC_PARROT_GetStatusMask,"GetStatusMask"},
	{PTP_OC_PARROT_EjectStorage,"EjectStorage"},
	{PTP_OC_PARROT_StartMagnetoCalib,"StartMagnetoCalib"},
	{PTP_OC_PARROT_StopMagnetoCalib,"StopMagnetoCalib"},
	{PTP_OC_PARROT_MagnetoCalibStatus,"MagnetoCalibStatus"},
	{PTP_OC_PARROT_SendFirmwareUpdate,"SendFirmwareUpdate"},
};

ptp_opcode_trans_t ptp_opcode_leica_trans[] = {
	{PTP_OC_LEICA_SetCameraSettings,"SetCameraSettings"},
	{PTP_OC_LEICA_GetCameraSettings,"GetCameraSettings"},
	{PTP_OC_LEICA_GetLensParameter,"GetLensParameter"},
	{PTP_OC_LEICA_LEReleaseStages,"LEReleaseStages"},
	{PTP_OC_LEICA_LEOpenSession,"LEOpenSession"},
	{PTP_OC_LEICA_LECloseSession,"LECloseSession"},
	{PTP_OC_LEICA_RequestObjectTransferReady,"RequestObjectTransferReady"},
	{PTP_OC_LEICA_GetGeoTrackingData,"GetGeoTrackingData"},
	{PTP_OC_LEICA_OpenDebugSession,"OpenDebugSession"},
	{PTP_OC_LEICA_CloseDebugSession,"CloseDebugSession"},
	{PTP_OC_LEICA_GetDebugBuffer,"GetDebugBuffer"},
	{PTP_OC_LEICA_DebugCommandString,"DebugCommandString"},
	{PTP_OC_LEICA_GetDebugRoute,"GetDebugRoute"},
	{PTP_OC_LEICA_SetIPTCData,"SetIPTCData"},
	{PTP_OC_LEICA_GetIPTCData,"GetIPTCData"},
	{PTP_OC_LEICA_LEControlAutoFocus,"LEControlAutoFocus"},
	{PTP_OC_LEICA_LEControlBulbExposure,"LEControlBulbExposure"},
	{PTP_OC_LEICA_LEControlContinuousExposure,"LEControlContinuousExposure"},
	{PTP_OC_LEICA_901b,"901b"},
	{PTP_OC_LEICA_LEControlPhotoLiveView,"LEControlPhotoLiveView"},
	{PTP_OC_LEICA_LEKeepSessionActive,"LEKeepSessionActive"},
	{PTP_OC_LEICA_LEMoveLens,"LEMoveLens"},
	{PTP_OC_LEICA_Get3DAxisData,"Get3DAxisData"},
	{PTP_OC_LEICA_LESetZoomMode,"LESetZoomMode"},
	{PTP_OC_LEICA_LESetFocusCrossPosition,"LESetFocusCrossPosition"},
	{PTP_OC_LEICA_LESetDisplayWindowPosition,"LESetDisplayWindowPosition"},
	{PTP_OC_LEICA_LEGetStreamData,"LEGetStreamData"},
	{PTP_OC_LEICA_OpenLiveViewSession,"OpenLiveViewSession"},
	{PTP_OC_LEICA_CloseLiveViewSession,"CloseLiveViewSession"},
	{PTP_OC_LEICA_LESetDateTime,"LESetDateTime"},
	{PTP_OC_LEICA_GetObjectPropListPaginated,"GetObjectPropListPaginated"},
	{PTP_OC_LEICA_OpenProductionSession,"OpenProductionSession"},
	{PTP_OC_LEICA_CloseProductionSession,"CloseProductionSession"},
	{PTP_OC_LEICA_UpdateFirmware,"UpdateFirmware"},
	{PTP_OC_LEICA_OpenOSDSession,"OpenOSDSession"},
	{PTP_OC_LEICA_CloseOSDSession,"CloseOSDSession"},
	{PTP_OC_LEICA_GetOSDData,"GetOSDData"},
	{PTP_OC_LEICA_GetFirmwareStruct,"GetFirmwareStruct"},
	{PTP_OC_LEICA_GetDebugMenu,"GetDebugMenu"},
	{PTP_OC_LEICA_SetDebugMenu,"SetDebugMenu"},
	{PTP_OC_LEICA_OdinMessage,"OdinMessage"},
	{PTP_OC_LEICA_GetDebugObjectHandles,"GetDebugObjectHandles"},
	{PTP_OC_LEICA_GetDebugObject,"GetDebugObject"},
	{PTP_OC_LEICA_DeleteDebugObject,"DeleteDebugObject"},
	{PTP_OC_LEICA_GetDebugObjectInfo,"GetDebugObjectInfo"},
	{PTP_OC_LEICA_WriteDebugObject,"WriteDebugObject"},
	{PTP_OC_LEICA_CreateDebugObject,"CreateDebugObject"},
	{PTP_OC_LEICA_Calibrate3DAxis,"Calibrate3DAxis"},
	{PTP_OC_LEICA_MagneticCalibration,"MagneticCalibration"},
	{PTP_OC_LEICA_GetViewFinderData,"GetViewFinderData"},
};

ptp_opcode_trans_t ptp_opcode_sigmafp_trans[] = {
	{PTP_OC_SIGMA_FP_GetCamConfig,"GetCamConfig"},
	{PTP_OC_SIGMA_FP_GetCamStatus,"GetCamStatus"},
	{PTP_OC_SIGMA_FP_GetDataGroup1,"GetDataGroup1"},
	{PTP_OC_SIGMA_FP_GetDataGroup2,"GetDataGroup2"},
	{PTP_OC_SIGMA_FP_GetDataGroup3,"GetDataGroup3"},
	{PTP_OC_SIGMA_FP_GetCaptureStatus,"GetCaptureStatus"},
	{PTP_OC_SIGMA_FP_SetDataGroup1,"SetDataGroup1"},
	{PTP_OC_SIGMA_FP_SetDataGroup2,"SetDataGroup2"},
	{PTP_OC_SIGMA_FP_SetDataGroup3,"SetDataGroup3"},
	{PTP_OC_SIGMA_FP_ClockAdjust,"ClockAdjust"},
	{PTP_OC_SIGMA_FP_Snap,"Snap"},
	{PTP_OC_SIGMA_FP_ClearImageDBSingle,"ClearImageDBSingle"},
	{PTP_OC_SIGMA_FP_ClearImageDBAll,"ClearImageDBAll"},
	{PTP_OC_SIGMA_FP_GetPictFileInfo,"GetPictFileInfo"},
	{PTP_OC_SIGMA_FP_GetPartialPictFile,"GetPartialPictFile"},
	{PTP_OC_SIGMA_FP_GetBigPartialPictFile,"GetBigPartialPictFile"},
	{PTP_OC_SIGMA_FP_GetDataGroup4,"GetDataGroup4"},
	{PTP_OC_SIGMA_FP_SetDataGroup4,"SetDataGroup4"},
	{PTP_OC_SIGMA_FP_GetCamSentInfo2,"GetCamSentInfo2"},
	{PTP_OC_SIGMA_FP_GetCamSentInfo3,"GetCamSentInfo3"},
	{PTP_OC_SIGMA_FP_GetDataGroup5,"GetDataGroup5"},
	{PTP_OC_SIGMA_FP_SetDataGroup5,"SetDataGroup5"},
	{PTP_OC_SIGMA_FP_GetDataGroup6,"GetDataGroup6"},
	{PTP_OC_SIGMA_FP_SetDataGroup6,"SetDataGroup6"},
	{PTP_OC_SIGMA_FP_GetCamViewFrame,"GetCamViewFrame"},
	{PTP_OC_SIGMA_FP_GetPictFileInfo2,"GetPictFileInfo2"},
	{PTP_OC_SIGMA_FP_GetCamCanSetInfo5,"GetCamCanSetInfo5"},
	{PTP_OC_SIGMA_FP_GetCamDataGroupFocus,"GetCamDataGroupFocus"},
	{PTP_OC_SIGMA_FP_SetCamDataGroupFocus,"SetCamDataGroupFocus"},
	{PTP_OC_SIGMA_FP_GetCamDataGroupMovie,"GetCamDataGroupMovie"},
	{PTP_OC_SIGMA_FP_SetCamDataGroupMovie,"SetCamDataGroupMovie"},
	{PTP_OC_SIGMA_FP_GetCameraInfo,"GetCameraInfo"},
	{PTP_OC_SIGMA_FP_GetMovieFileInfo,"GetMovieFileInfo"},
	{PTP_OC_SIGMA_FP_GetPartialMovieFile,"GetPartialMovieFile"},
};

const char*
ptp_get_opcode_name(PTPParams* params, uint16_t opcode)
{
#define RETURN_NAME_FROM_TABLE(TABLE, OPCODE) \
{ \
	unsigned int i; \
	for (i=0; i<ARRAYSIZE(TABLE); i++) \
		if (OPCODE == TABLE[i].opcode) \
			return _(TABLE[i].name); \
	return _("Unknown PTP_OC"); \
}

	if (!(opcode & 0x8000))
		RETURN_NAME_FROM_TABLE(ptp_opcode_trans, opcode);

	switch (params->deviceinfo.VendorExtensionID) {
	case PTP_VENDOR_MICROSOFT:
	case PTP_VENDOR_PANASONIC:
	case PTP_VENDOR_MTP:	RETURN_NAME_FROM_TABLE(ptp_opcode_mtp_trans, opcode);
	case PTP_VENDOR_NIKON:	RETURN_NAME_FROM_TABLE(ptp_opcode_nikon_trans, opcode);
	case PTP_VENDOR_CANON:	RETURN_NAME_FROM_TABLE(ptp_opcode_canon_trans, opcode);
	case PTP_VENDOR_SONY:	RETURN_NAME_FROM_TABLE(ptp_opcode_sony_trans, opcode);
	case PTP_VENDOR_PARROT:	RETURN_NAME_FROM_TABLE(ptp_opcode_parrot_trans, opcode);
	case PTP_VENDOR_GP_LEICA:	RETURN_NAME_FROM_TABLE(ptp_opcode_leica_trans, opcode);
	case PTP_VENDOR_GP_SIGMAFP:	RETURN_NAME_FROM_TABLE(ptp_opcode_sigmafp_trans, opcode);
	default:
		break;
	}
#undef RETURN_NAME_FROM_TABLE

	return _("Unknown VendorExtensionID");
}


struct {
	uint16_t code;
	uint16_t vendor;
	const char *name;
} ptp_event_codes[] = {
	{PTP_EC_Undefined, 		0, "Undefined"},
	{PTP_EC_CancelTransaction,	0, "CancelTransaction"},
	{PTP_EC_ObjectAdded,		0, "ObjectAdded"},
	{PTP_EC_ObjectRemoved,		0, "ObjectRemoved"},
	{PTP_EC_StoreAdded,		0, "StoreAdded"},
	{PTP_EC_StoreRemoved,		0, "StoreRemoved"},
	{PTP_EC_DevicePropChanged,	0, "DevicePropChanged"},
	{PTP_EC_ObjectInfoChanged,	0, "ObjectInfoChanged"},
	{PTP_EC_DeviceInfoChanged,	0, "DeviceInfoChanged"},
	{PTP_EC_RequestObjectTransfer,	0, "RequestObjectTransfer"},
	{PTP_EC_StoreFull,		0, "StoreFull"},
	{PTP_EC_DeviceReset,		0, "DeviceReset"},
	{PTP_EC_StorageInfoChanged,	0, "StorageInfoChanged"},
	{PTP_EC_CaptureComplete,	0, "CaptureComplete"},
	{PTP_EC_UnreportedStatus,	0, "UnreportedStatus"},

	{PTP_EC_MTP_ObjectPropChanged,		0, "ObjectPropChanged"},
	{PTP_EC_MTP_ObjectPropDescChanged,	0, "ObjectPropDescChanged"},
	{PTP_EC_MTP_ObjectReferencesChanged,	0, "ObjectReferencesChanged"},

	{PTP_EC_Nikon_ObjectAddedInSDRAM, 		PTP_VENDOR_NIKON, "Nikon_ObjectAddedInSDRAM"},
	{PTP_EC_Nikon_CaptureCompleteRecInSdram,	PTP_VENDOR_NIKON, "Nikon_CaptureCompleteRecInSdram"},
	{PTP_EC_Nikon_AdvancedTransfer, 		PTP_VENDOR_NIKON, "Nikon_AdvancedTransfer"},
	{PTP_EC_Nikon_PreviewImageAdded, 		PTP_VENDOR_NIKON, "Nikon_PreviewImageAdded"},
	{PTP_EC_Nikon_MovieRecordInterrupted,		PTP_VENDOR_NIKON, "Nikon_MovieRecordInterrupted"},
	{PTP_EC_Nikon_MovieRecordComplete,		PTP_VENDOR_NIKON, "Nikon_MovieRecordComplete"},
	{PTP_EC_Nikon_MovieRecordStarted,		PTP_VENDOR_NIKON, "Nikon_MovieRecordStarted"},
	{PTP_EC_Nikon_PictureControlAdjustChanged,	PTP_VENDOR_NIKON, "Nikon_PictureControlAdjustChanged"},
	{PTP_EC_Nikon_LiveViewStateChanged,		PTP_VENDOR_NIKON, "Nikon_LiveViewStateChanged"},
	{PTP_EC_Nikon_ManualSettingsLensDataChanged,	PTP_VENDOR_NIKON, "Nikon_ManualSettingsLensDataChanged"},
	{PTP_EC_Nikon_ActiveSelectionInterrupted,	PTP_VENDOR_NIKON, "Nikon_ActiveSelectionInterrupted"},
	{PTP_EC_Nikon_SBAdded,				PTP_VENDOR_NIKON, "Nikon_SBAdded"},
	{PTP_EC_Nikon_SBRemoved,			PTP_VENDOR_NIKON, "Nikon_SBRemoved"},
	{PTP_EC_Nikon_SBAttrChanged,			PTP_VENDOR_NIKON, "Nikon_SBAttrChanged"},
	{PTP_EC_Nikon_SBGroupAttrChanged,		PTP_VENDOR_NIKON, "Nikon_SBGroupAttrChanged"},
	{PTP_EC_Nikon_1stCaptureComplete,		PTP_VENDOR_NIKON, "Nikon_1stCaptureComplete"},
	{PTP_EC_Nikon_MirrorUpCancelComplete,		PTP_VENDOR_NIKON, "Nikon_MirrorUpCancelComplete"},

	{PTP_EC_Sony_ObjectAdded,			PTP_VENDOR_SONY, "Sony_ObjectAdded"},
	{PTP_EC_Sony_ObjectRemoved,			PTP_VENDOR_SONY, "Sony_ObjectRemoved"},
	{PTP_EC_Sony_DevicePropChanged,			PTP_VENDOR_SONY, "Sony_DevicePropChanged"},
	{PTP_EC_Sony_CapturedEvent,			PTP_VENDOR_SONY, "Sony_CapturedEvent"},
	{PTP_EC_Sony_CWBCaptureResult,			PTP_VENDOR_SONY, "Sony_CWBCaptureResult"},
	{PTP_EC_Sony_CameraSettingReadResult,		PTP_VENDOR_SONY, "Sony_CameraSettingReadResult"},
	{PTP_EC_Sony_FTPSettingReadResult,		PTP_VENDOR_SONY, "Sony_FTPSettingReadResult"},
	{PTP_EC_Sony_MediaFormatResult,			PTP_VENDOR_SONY, "Sony_MediaFormatResult"},
	{PTP_EC_Sony_FTPDisplayNameListChanged,		PTP_VENDOR_SONY, "Sony_FTPDisplayNameListChanged"},
	{PTP_EC_Sony_ContentsTransferEvent,		PTP_VENDOR_SONY, "Sony_ContentsTransferEvent"},
	{PTP_EC_Sony_DisplayListChangedEvent,		PTP_VENDOR_SONY, "Sony_DisplayListChangedEvent"},
	{PTP_EC_Sony_FocusPositionResult,		PTP_VENDOR_SONY, "Sony_FocusPositionResult"},
	{PTP_EC_Sony_LensInformationChanged,		PTP_VENDOR_SONY, "Sony_LensInformationChanged"},
	{PTP_EC_Sony_OperationResult,			PTP_VENDOR_SONY, "Sony_OperationResult"},
	{PTP_EC_Sony_AFStatus,				PTP_VENDOR_SONY, "Sony_AFStatus"},
	{PTP_EC_Sony_MovieRecOperationsResult,		PTP_VENDOR_SONY, "Sony_MovieRecOperationsResult"},

	{PTP_EC_Olympus_CreateRecView,			PTP_VENDOR_GP_OLYMPUS, "Olympus_CreateRecView"},
	{PTP_EC_Olympus_CreateRecView_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_CreateRecView_New"},
	{PTP_EC_Olympus_ObjectAdded,			PTP_VENDOR_GP_OLYMPUS, "Olympus_ObjectAdded"},
	{PTP_EC_Olympus_ObjectAdded_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_ObjectAdded_New"},
	{PTP_EC_Olympus_AF_Frame,			PTP_VENDOR_GP_OLYMPUS, "Olympus_AF_Frame"},
	{PTP_EC_Olympus_AF_Frame_New,			PTP_VENDOR_GP_OLYMPUS, "Olympus_AF_Frame_New"},
	{PTP_EC_Olympus_DirectStoreImage,		PTP_VENDOR_GP_OLYMPUS, "Olympus_DirectStoreImage"},
	{PTP_EC_Olympus_DirectStoreImage_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_DirectStoreImage_New"},
	{PTP_EC_Olympus_ComplateCameraControlOff,	PTP_VENDOR_GP_OLYMPUS, "Olympus_ComplateCameraControlOff"},
	{PTP_EC_Olympus_ComplateCameraControlOff_New,	PTP_VENDOR_GP_OLYMPUS, "Olympus_ComplateCameraControlOff_New"},
	{PTP_EC_Olympus_AF_Frame_Over_Info,		PTP_VENDOR_GP_OLYMPUS, "Olympus_AF_Frame_Over_Info"},
	{PTP_EC_Olympus_AF_Frame_Over_Info_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_AF_Frame_Over_Info_New"},
	{PTP_EC_Olympus_DevicePropChanged,		PTP_VENDOR_GP_OLYMPUS, "Olympus_DevicePropChanged"},
	{PTP_EC_Olympus_DevicePropChanged_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_DevicePropChanged_New"},
	{PTP_EC_Olympus_ImageTransferModeFinish,	PTP_VENDOR_GP_OLYMPUS, "Olympus_ImageTransferModeFinish"},
	{PTP_EC_Olympus_ImageTransferModeFinish_New,	PTP_VENDOR_GP_OLYMPUS, "Olympus_ImageTransferModeFinish_New"},
	{PTP_EC_Olympus_ImageRecordFinish,		PTP_VENDOR_GP_OLYMPUS, "Olympus_ImageRecordFinish"},
	{PTP_EC_Olympus_ImageRecordFinish_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_ImageRecordFinish_New"},
	{PTP_EC_Olympus_SlotStatusChange,		PTP_VENDOR_GP_OLYMPUS, "Olympus_SlotStatusChange"},
	{PTP_EC_Olympus_SlotStatusChange_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_SlotStatusChange_New"},
	{PTP_EC_Olympus_PrioritizeRecord,		PTP_VENDOR_GP_OLYMPUS, "Olympus_PrioritizeRecord"},
	{PTP_EC_Olympus_PrioritizeRecord_New,		PTP_VENDOR_GP_OLYMPUS, "Olympus_PrioritizeRecord_New"},
	{PTP_EC_Olympus_FailCombiningAfterShooting,	PTP_VENDOR_GP_OLYMPUS, "Olympus_FailCombiningAfterShooting"},
	{PTP_EC_Olympus_FailCombiningAfterShooting_New,	PTP_VENDOR_GP_OLYMPUS, "Olympus_FailCombiningAfterShooting_New"},
	{PTP_EC_Olympus_NotifyAFTargetFrame,		PTP_VENDOR_GP_OLYMPUS, "Olympus_NotifyAFTargetFrame"},
	{PTP_EC_Olympus_NotifyAFTargetFrame_New,	PTP_VENDOR_GP_OLYMPUS, "Olympus_NotifyAFTargetFrame_New"},
	{PTP_EC_Olympus_RawEditParamChanged,		PTP_VENDOR_GP_OLYMPUS, "Olympus_RawEditParamChanged"},
	{PTP_EC_Olympus_OlyNotifyCreateDrawEdit,	PTP_VENDOR_GP_OLYMPUS, "Olympus_OlyNotifyCreateDrawEdit"},
	{PTP_EC_Olympus_PropertyChanged,		PTP_VENDOR_GP_OLYMPUS, "Olympus_PropertyChanged"},
	{PTP_EC_Olympus_CaptureComplete,		PTP_VENDOR_GP_OLYMPUS, "Olympus_CaptureComplete"},

};

const char*
ptp_get_event_code_name(PTPParams* params, uint16_t event_code)
{
	unsigned int	i;
	uint16_t	vendor = params->deviceinfo.VendorExtensionID;

	for (i=0; i<ARRAYSIZE(ptp_event_codes); i++)
		if ((ptp_event_codes[i].code == event_code) && ((ptp_event_codes[i].vendor == 0) || (ptp_event_codes[i].vendor == vendor)))
			return ptp_event_codes[i].name;
	return "Unknown Event";
}

const char*
ptp_get_eos_event_name(PTPParams *params, enum _PTPCanonEOSEventType type)
{
	switch (type)
	{
	case PTP_EOSEvent_Unknown: return "Unknown";
	case PTP_EOSEvent_PropertyChanged: return "PropertyChanged";
	case PTP_EOSEvent_CameraStatus: return "CameraStatus";
	case PTP_EOSEvent_FocusInfo: return "FocusInfo";
	case PTP_EOSEvent_FocusMask: return "FocusMask";
	case PTP_EOSEvent_ObjectTransfer: return "ObjectTransfer";
	case PTP_EOSEvent_ObjectAdded: return "ObjectAdded";
	case PTP_EOSEvent_ObjectRemoved: return "ObjectRemoved";
	case PTP_EOSEvent_ObjectInfoChanged: return "ObjectInfoChanged";
	case PTP_EOSEvent_ObjectContentChanged: return "ObjectContentChanged";
	default:
		ptp_error (params, "Unknown PTPCanonEOSEventType: bug in libgphoto2");
		return "InvalidEOSEventType";
	}
}

struct {
	uint16_t id;
	const char *name;
} ptp_opc_trans[] = {
	{PTP_OPC_StorageID,"StorageID"},
	{PTP_OPC_ObjectFormat,"ObjectFormat"},
	{PTP_OPC_ProtectionStatus,"ProtectionStatus"},
	{PTP_OPC_ObjectSize,"ObjectSize"},
	{PTP_OPC_AssociationType,"AssociationType"},
	{PTP_OPC_AssociationDesc,"AssociationDesc"},
	{PTP_OPC_ObjectFileName,"ObjectFileName"},
	{PTP_OPC_DateCreated,"DateCreated"},
	{PTP_OPC_DateModified,"DateModified"},
	{PTP_OPC_Keywords,"Keywords"},
	{PTP_OPC_ParentObject,"ParentObject"},
	{PTP_OPC_AllowedFolderContents,"AllowedFolderContents"},
	{PTP_OPC_Hidden,"Hidden"},
	{PTP_OPC_SystemObject,"SystemObject"},
	{PTP_OPC_PersistantUniqueObjectIdentifier,"PersistantUniqueObjectIdentifier"},
	{PTP_OPC_SyncID,"SyncID"},
	{PTP_OPC_PropertyBag,"PropertyBag"},
	{PTP_OPC_Name,"Name"},
	{PTP_OPC_CreatedBy,"CreatedBy"},
	{PTP_OPC_Artist,"Artist"},
	{PTP_OPC_DateAuthored,"DateAuthored"},
	{PTP_OPC_Description,"Description"},
	{PTP_OPC_URLReference,"URLReference"},
	{PTP_OPC_LanguageLocale,"LanguageLocale"},
	{PTP_OPC_CopyrightInformation,"CopyrightInformation"},
	{PTP_OPC_Source,"Source"},
	{PTP_OPC_OriginLocation,"OriginLocation"},
	{PTP_OPC_DateAdded,"DateAdded"},
	{PTP_OPC_NonConsumable,"NonConsumable"},
	{PTP_OPC_CorruptOrUnplayable,"CorruptOrUnplayable"},
	{PTP_OPC_ProducerSerialNumber,"ProducerSerialNumber"},
	{PTP_OPC_RepresentativeSampleFormat,"RepresentativeSampleFormat"},
	{PTP_OPC_RepresentativeSampleSize,"RepresentativeSampleSize"},
	{PTP_OPC_RepresentativeSampleHeight,"RepresentativeSampleHeight"},
	{PTP_OPC_RepresentativeSampleWidth,"RepresentativeSampleWidth"},
	{PTP_OPC_RepresentativeSampleDuration,"RepresentativeSampleDuration"},
	{PTP_OPC_RepresentativeSampleData,"RepresentativeSampleData"},
	{PTP_OPC_Width,"Width"},
	{PTP_OPC_Height,"Height"},
	{PTP_OPC_Duration,"Duration"},
	{PTP_OPC_Rating,"Rating"},
	{PTP_OPC_Track,"Track"},
	{PTP_OPC_Genre,"Genre"},
	{PTP_OPC_Credits,"Credits"},
	{PTP_OPC_Lyrics,"Lyrics"},
	{PTP_OPC_SubscriptionContentID,"SubscriptionContentID"},
	{PTP_OPC_ProducedBy,"ProducedBy"},
	{PTP_OPC_UseCount,"UseCount"},
	{PTP_OPC_SkipCount,"SkipCount"},
	{PTP_OPC_LastAccessed,"LastAccessed"},
	{PTP_OPC_ParentalRating,"ParentalRating"},
	{PTP_OPC_MetaGenre,"MetaGenre"},
	{PTP_OPC_Composer,"Composer"},
	{PTP_OPC_EffectiveRating,"EffectiveRating"},
	{PTP_OPC_Subtitle,"Subtitle"},
	{PTP_OPC_OriginalReleaseDate,"OriginalReleaseDate"},
	{PTP_OPC_AlbumName,"AlbumName"},
	{PTP_OPC_AlbumArtist,"AlbumArtist"},
	{PTP_OPC_Mood,"Mood"},
	{PTP_OPC_DRMStatus,"DRMStatus"},
	{PTP_OPC_SubDescription,"SubDescription"},
	{PTP_OPC_IsCropped,"IsCropped"},
	{PTP_OPC_IsColorCorrected,"IsColorCorrected"},
	{PTP_OPC_ImageBitDepth,"ImageBitDepth"},
	{PTP_OPC_Fnumber,"Fnumber"},
	{PTP_OPC_ExposureTime,"ExposureTime"},
	{PTP_OPC_ExposureIndex,"ExposureIndex"},
	{PTP_OPC_DisplayName,"DisplayName"},
	{PTP_OPC_BodyText,"BodyText"},
	{PTP_OPC_Subject,"Subject"},
	{PTP_OPC_Priority,"Priority"},
	{PTP_OPC_GivenName,"GivenName"},
	{PTP_OPC_MiddleNames,"MiddleNames"},
	{PTP_OPC_FamilyName,"FamilyName"},

	{PTP_OPC_Prefix,"Prefix"},
	{PTP_OPC_Suffix,"Suffix"},
	{PTP_OPC_PhoneticGivenName,"PhoneticGivenName"},
	{PTP_OPC_PhoneticFamilyName,"PhoneticFamilyName"},
	{PTP_OPC_EmailPrimary,"EmailPrimary"},
	{PTP_OPC_EmailPersonal1,"EmailPersonal1"},
	{PTP_OPC_EmailPersonal2,"EmailPersonal2"},
	{PTP_OPC_EmailBusiness1,"EmailBusiness1"},
	{PTP_OPC_EmailBusiness2,"EmailBusiness2"},
	{PTP_OPC_EmailOthers,"EmailOthers"},
	{PTP_OPC_PhoneNumberPrimary,"PhoneNumberPrimary"},
	{PTP_OPC_PhoneNumberPersonal,"PhoneNumberPersonal"},
	{PTP_OPC_PhoneNumberPersonal2,"PhoneNumberPersonal2"},
	{PTP_OPC_PhoneNumberBusiness,"PhoneNumberBusiness"},
	{PTP_OPC_PhoneNumberBusiness2,"PhoneNumberBusiness2"},
	{PTP_OPC_PhoneNumberMobile,"PhoneNumberMobile"},
	{PTP_OPC_PhoneNumberMobile2,"PhoneNumberMobile2"},
	{PTP_OPC_FaxNumberPrimary,"FaxNumberPrimary"},
	{PTP_OPC_FaxNumberPersonal,"FaxNumberPersonal"},
	{PTP_OPC_FaxNumberBusiness,"FaxNumberBusiness"},
	{PTP_OPC_PagerNumber,"PagerNumber"},
	{PTP_OPC_PhoneNumberOthers,"PhoneNumberOthers"},
	{PTP_OPC_PrimaryWebAddress,"PrimaryWebAddress"},
	{PTP_OPC_PersonalWebAddress,"PersonalWebAddress"},
	{PTP_OPC_BusinessWebAddress,"BusinessWebAddress"},
	{PTP_OPC_InstantMessengerAddress,"InstantMessengerAddress"},
	{PTP_OPC_InstantMessengerAddress2,"InstantMessengerAddress2"},
	{PTP_OPC_InstantMessengerAddress3,"InstantMessengerAddress3"},
	{PTP_OPC_PostalAddressPersonalFull,"PostalAddressPersonalFull"},
	{PTP_OPC_PostalAddressPersonalFullLine1,"PostalAddressPersonalFullLine1"},
	{PTP_OPC_PostalAddressPersonalFullLine2,"PostalAddressPersonalFullLine2"},
	{PTP_OPC_PostalAddressPersonalFullCity,"PostalAddressPersonalFullCity"},
	{PTP_OPC_PostalAddressPersonalFullRegion,"PostalAddressPersonalFullRegion"},
	{PTP_OPC_PostalAddressPersonalFullPostalCode,"PostalAddressPersonalFullPostalCode"},
	{PTP_OPC_PostalAddressPersonalFullCountry,"PostalAddressPersonalFullCountry"},
	{PTP_OPC_PostalAddressBusinessFull,"PostalAddressBusinessFull"},
	{PTP_OPC_PostalAddressBusinessLine1,"PostalAddressBusinessLine1"},
	{PTP_OPC_PostalAddressBusinessLine2,"PostalAddressBusinessLine2"},
	{PTP_OPC_PostalAddressBusinessCity,"PostalAddressBusinessCity"},
	{PTP_OPC_PostalAddressBusinessRegion,"PostalAddressBusinessRegion"},
	{PTP_OPC_PostalAddressBusinessPostalCode,"PostalAddressBusinessPostalCode"},
	{PTP_OPC_PostalAddressBusinessCountry,"PostalAddressBusinessCountry"},
	{PTP_OPC_PostalAddressOtherFull,"PostalAddressOtherFull"},
	{PTP_OPC_PostalAddressOtherLine1,"PostalAddressOtherLine1"},
	{PTP_OPC_PostalAddressOtherLine2,"PostalAddressOtherLine2"},
	{PTP_OPC_PostalAddressOtherCity,"PostalAddressOtherCity"},
	{PTP_OPC_PostalAddressOtherRegion,"PostalAddressOtherRegion"},
	{PTP_OPC_PostalAddressOtherPostalCode,"PostalAddressOtherPostalCode"},
	{PTP_OPC_PostalAddressOtherCountry,"PostalAddressOtherCountry"},
	{PTP_OPC_OrganizationName,"OrganizationName"},
	{PTP_OPC_PhoneticOrganizationName,"PhoneticOrganizationName"},
	{PTP_OPC_Role,"Role"},
	{PTP_OPC_Birthdate,"Birthdate"},
	{PTP_OPC_MessageTo,"MessageTo"},
	{PTP_OPC_MessageCC,"MessageCC"},
	{PTP_OPC_MessageBCC,"MessageBCC"},
	{PTP_OPC_MessageRead,"MessageRead"},
	{PTP_OPC_MessageReceivedTime,"MessageReceivedTime"},
	{PTP_OPC_MessageSender,"MessageSender"},
	{PTP_OPC_ActivityBeginTime,"ActivityBeginTime"},
	{PTP_OPC_ActivityEndTime,"ActivityEndTime"},
	{PTP_OPC_ActivityLocation,"ActivityLocation"},
	{PTP_OPC_ActivityRequiredAttendees,"ActivityRequiredAttendees"},
	{PTP_OPC_ActivityOptionalAttendees,"ActivityOptionalAttendees"},
	{PTP_OPC_ActivityResources,"ActivityResources"},
	{PTP_OPC_ActivityAccepted,"ActivityAccepted"},
	{PTP_OPC_Owner,"Owner"},
	{PTP_OPC_Editor,"Editor"},
	{PTP_OPC_Webmaster,"Webmaster"},
	{PTP_OPC_URLSource,"URLSource"},
	{PTP_OPC_URLDestination,"URLDestination"},
	{PTP_OPC_TimeBookmark,"TimeBookmark"},
	{PTP_OPC_ObjectBookmark,"ObjectBookmark"},
	{PTP_OPC_ByteBookmark,"ByteBookmark"},
	{PTP_OPC_LastBuildDate,"LastBuildDate"},
	{PTP_OPC_TimetoLive,"TimetoLive"},
	{PTP_OPC_MediaGUID,"MediaGUID"},
	{PTP_OPC_TotalBitRate,"TotalBitRate"},
	{PTP_OPC_BitRateType,"BitRateType"},
	{PTP_OPC_SampleRate,"SampleRate"},
	{PTP_OPC_NumberOfChannels,"NumberOfChannels"},
	{PTP_OPC_AudioBitDepth,"AudioBitDepth"},
	{PTP_OPC_ScanDepth,"ScanDepth"},
	{PTP_OPC_AudioWAVECodec,"AudioWAVECodec"},
	{PTP_OPC_AudioBitRate,"AudioBitRate"},
	{PTP_OPC_VideoFourCCCodec,"VideoFourCCCodec"},
	{PTP_OPC_VideoBitRate,"VideoBitRate"},
	{PTP_OPC_FramesPerThousandSeconds,"FramesPerThousandSeconds"},
	{PTP_OPC_KeyFrameDistance,"KeyFrameDistance"},
	{PTP_OPC_BufferSize,"BufferSize"},
	{PTP_OPC_EncodingQuality,"EncodingQuality"},
	{PTP_OPC_EncodingProfile,"EncodingProfile"},
	{PTP_OPC_BuyFlag,"BuyFlag"},
};

int
ptp_render_mtp_propname(uint16_t propid, int spaceleft, char *txt)
{
	unsigned int i;
	for (i=0;i<ARRAYSIZE(ptp_opc_trans);i++)
		if (propid == ptp_opc_trans[i].id)
			return snprintf(txt, spaceleft, "%s", ptp_opc_trans[i].name);
	return snprintf (txt, spaceleft,"unknown(%04x)", propid);
}

void
ptp_free_object_prop(MTPObjectProp *prop)
{
	ptp_free_propvalue(prop->DataType, &prop->Value);
}

#if 0
/*
 * Allocate and default-initialize a few object properties.
 */
MTPObjectProp *
ptp_get_new_object_prop_entry(MTPObjectProp **props, int *nrofprops)
{
	MTPObjectProp *newprops;
	MTPObjectProp *prop;

	newprops = realloc(*props,sizeof(MTPObjectProp)*(*nrofprops+1));
	if (newprops == NULL)
		return NULL;
	prop = &newprops[*nrofprops];
	prop->PropCode = PTP_OPC_StorageID; /* Should be "unknown" */
	prop->DataType = PTP_DTC_UNDEF;
	prop->ObjectHandle = 0x00000000U;
	prop->Value.str = NULL;

	(*props) = newprops;
	(*nrofprops)++;
	return prop;
}

/*
 * Find a certain object property in the cache, i.e. a certain metadata
 * item for a certain object handle.
 */
MTPObjectProp *
ptp_find_object_prop_in_cache(PTPParams *params, uint32_t const handle, uint32_t const attribute_id)
{
	PTPObject	*ob;
	uint16_t	ret;

	ret = ptp_find_object_in_cache (params, handle, &ob);
	if (ret != PTP_RC_OK)
		return NULL;
	for_each (MTPObjectProp*, prop, ob->mtp_props)
		if (attribute_id == prop->PropCode)
			return prop;
	return NULL;
}
#endif

uint16_t
ptp_remove_object_from_cache(PTPParams *params, uint32_t handle)
{
	PTPObject	*ob;

	CHECK_PTP_RC(ptp_find_object_in_cache (params, handle, &ob));
	ptp_free_object (ob);
	array_remove(&params->objects, ob);

	return PTP_RC_OK;
}

static int _cmp_ob (const void *a, const void *b)
{
	PTPObject *oa = (PTPObject*)a;
	PTPObject *ob = (PTPObject*)b;

	/* Do not subtract the oids and return ...
	 * the unsigned int -> int conversion will overflow in cases
	 * like 0xfffc0000 vs 0x0004000. */
	if (oa->oid > ob->oid) return 1;
	if (oa->oid < ob->oid) return -1;
	return 0;
}

void
ptp_objects_sort (PTPParams *params)
{
	qsort (params->objects.val, params->objects.len, sizeof(PTPObject), _cmp_ob);
}

/* Binary search in objects. Needs "objects" to be a sorted by oid!  */
uint16_t
ptp_find_object_in_cache (PTPParams *params, uint32_t handle, PTPObject **retob)
{
	PTPObject	tmpob;

	tmpob.oid = handle;
	*retob = bsearch (&tmpob, params->objects.val, params->objects.len, sizeof(tmpob), _cmp_ob);
	if (!*retob)
		return PTP_RC_GeneralError;
	return PTP_RC_OK;
}

/* Binary search in objects + insert if not found. Needs "objects" to be a sorted by oid!  */
uint16_t
ptp_find_or_insert_object_in_cache (PTPParams *params, uint32_t handle, PTPObject **retob)
{
	unsigned int 	begin, end, cursor;
	unsigned int	insertat;

	if (!handle) return PTP_RC_GeneralError;
	*retob = NULL;
	if (!params->objects.len) {
		array_push_back_empty (&params->objects, retob);
		(*retob)->oid = handle;
		(*retob)->oi.Handle = handle;
		return PTP_RC_OK;
	}
	begin = 0;
	end = params->objects.len-1;
	/*ptp_debug (params, "searching %08x, total=%d", handle, params->objects.len);*/
	while (1) {
		cursor = (end-begin)/2+begin;
		/*ptp_debug (params, "ob %d: %08x [%d-%d]", cursor, params->objects[cursor].oid, begin, end);*/
		if (params->objects.val[cursor].oid == handle) {
			*retob = &params->objects.val[cursor];
			return PTP_RC_OK;
		}
		if (params->objects.val[cursor].oid < handle)
			begin = cursor;
		else
			end = cursor;
		if ((end - begin) <= 1)
			break;
	}
	if (params->objects.val[begin].oid == handle) {
		*retob = &params->objects.val[begin];
		return PTP_RC_OK;
	}
	if (params->objects.val[end].oid == handle) {
		*retob = &params->objects.val[end];
		return PTP_RC_OK;
	}
	if ((begin == 0) && (handle < params->objects.val[0].oid)) {
		insertat=begin;
	} else {
		if ((end == params->objects.len-1) && (handle > params->objects.val[end].oid))
			insertat=end+1;
		else
			insertat=begin+1;
	}
	/*ptp_debug (params, "inserting oid %x at [%x,%x], begin=%d, end=%d, insertat=%d\n", handle, params->objects[begin].oid, params->objects[end].oid, begin, end, insertat);*/
	array_extend_capacity (&params->objects, 1);
	if (insertat < params->objects.len)
		memmove (&params->objects.val[insertat+1], &params->objects.val[insertat], (params->objects.len-insertat)*sizeof(PTPObject));
	*retob = &params->objects.val[insertat];
	memset(*retob, 0, sizeof(PTPObject));
	(*retob)->oid = handle;
	(*retob)->oi.Handle = handle;
	params->objects.len++;
	return PTP_RC_OK;
}

uint16_t
ptp_object_want (PTPParams *params, uint32_t handle, unsigned int want, PTPObject **retob)
{
	uint16_t	ret;
	PTPObject	*ob;

	/* If GetObjectInfo is broken, force GetPropList */
	if (params->device_flags & DEVICE_FLAG_PROPLIST_OVERRIDES_OI)
		want |= PTPOBJECT_MTPPROPLIST_LOADED;

	*retob = NULL;
	if (!handle) {
		ptp_debug (params, "ptp_object_want: querying handle 0?\n");
		return PTP_RC_GeneralError;
	}
	CHECK_PTP_RC(ptp_find_or_insert_object_in_cache (params, handle, &ob));
	*retob = ob;
	/* Do we have all of it already? */
	if ((ob->flags & want) == want)
		return PTP_RC_OK;

#define X (PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_STORAGEID_LOADED|PTPOBJECT_PARENTOBJECT_LOADED)
	if ((want & X) && ((ob->flags & X) != X)) {
		uint32_t	saveparent = 0;

		/* One EOS issue, where getobjecthandles(root) returns obs without root flag. */
		if (ob->flags & PTPOBJECT_PARENTOBJECT_LOADED)
			saveparent = ob->oi.ParentObject;

		ret = ptp_getobjectinfo (params, handle, &ob->oi);
		if (ret != PTP_RC_OK) {
			/* kill it from the internal list ... */
			ptp_remove_object_from_cache(params, handle);
			return ret;
		}
		if (!ob->oi.Filename)
			ob->oi.Filename = strdup("<none>");
		if (ob->flags & PTPOBJECT_PARENTOBJECT_LOADED) {
			if (ob->oi.ParentObject != saveparent)
				ptp_debug (params, "saved parent %08x is not the same as read via getobjectinfo %08x", ob->oi.ParentObject, saveparent);
			ob->oi.ParentObject = saveparent;
		}

		/* Second EOS issue, 0x20000000 has 0x20000000 as parent */
		if (ob->oi.ParentObject == handle)
			ob->oi.ParentObject = 0;

		/* Detect if the file is larger than 4GB ... indicator is size 0xffffffff ...
		 * In that case explicitly request the MTP object proplist to get the right size */
		if (ob->oi.ObjectSize == 0xffffffffUL) {
			uint64_t	newsize;
			if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_NIKON)	&&
				ptp_operation_issupported(params,PTP_OC_NIKON_GetObjectSize)	&&
				(PTP_RC_OK == ptp_nikon_getobjectsize(params, handle, &newsize))
			) {
				ob->oi.ObjectSize = newsize;
			} else {
				/* more methods like e.g. for Canon */
				/* if not try MTP method */
				want |= PTPOBJECT_MTPPROPLIST_LOADED;
				params->device_flags |= DEVICE_FLAG_PROPLIST_OVERRIDES_OI; /* FIXME: wild hack so below code works, needs review. */
			}
		}

		/* Apple iOS X does that for the root folder. */
		if (ob->oi.ParentObject == ob->oi.StorageID) {
			PTPObject *parentob;

			if (ptp_find_object_in_cache (params, ob->oi.ParentObject, &parentob) != PTP_RC_OK) {
				ptp_debug (params, "parent %08x of %s has same id as storage id. and no object found ... rewriting to 0.", ob->oi.ParentObject, ob->oi.Filename);
				ob->oi.ParentObject = 0;
			}
		}

		/* Read out the canon special flags */
		if ((params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
		    ptp_operation_issupported(params,PTP_OC_CANON_GetObjectInfoEx)) {
			PTPCANONFolderEntry *ents = NULL;
			uint32_t            numents = 0;

			ret = ptp_canon_getobjectinfo(params,
				ob->oi.StorageID,0,
				ob->oi.ParentObject,handle,
				&ents,&numents
			);
			if ((ret == PTP_RC_OK) && (numents >= 1))
				ob->canon_flags = ents[0].Flags;
			free (ents);
		}

		ob->flags |= X;
	}
#undef X

	if ((params->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST) ||
	    !ptp_operation_issupported(params, PTP_OC_MTP_GetObjPropList)
	) {
		want &= ~PTPOBJECT_MTPPROPLIST_LOADED;
	}

	if ((want & PTPOBJECT_MTPPROPLIST_LOADED) && (!(ob->flags & PTPOBJECT_MTPPROPLIST_LOADED))
	) {
		ptp_debug (params, "ptp2/mtpfast: reading mtp proplist of %08x", handle);
		/* We just want this one object, not all at once. */
		if (PTP_RC_OK == ptp_mtp_getobjectproplist_single (params, handle, &ob->mtp_props))
			ob->flags |= PTPOBJECT_MTPPROPLIST_LOADED;

		/* Override the ObjectInfo data with data from properties */
		if ((ob->flags & PTPOBJECT_MTPPROPLIST_LOADED) && (params->device_flags & DEVICE_FLAG_PROPLIST_OVERRIDES_OI)) {

			for_each (MTPObjectProp*, prop, ob->mtp_props) {
				/* in case we got all subtree objects.
				 * FIXME: we explicitly requested props for a single object, so this seems outdated. */
				if (prop->ObjectHandle != handle) continue;

				switch (prop->PropCode) {
				case PTP_OPC_StorageID:
					ob->oi.StorageID = prop->Value.u32;
					break;
				case PTP_OPC_ObjectFormat:
					ob->oi.ObjectFormat = prop->Value.u16;
					break;
				case PTP_OPC_ProtectionStatus:
					ob->oi.ProtectionStatus = prop->Value.u16;
					break;
				case PTP_OPC_ObjectSize:
					if (prop->DataType == PTP_DTC_UINT64) {
						ob->oi.ObjectSize = prop->Value.u64;
					} else if (prop->DataType == PTP_DTC_UINT32) {
						ob->oi.ObjectSize = prop->Value.u32;
					}
					break;
				case PTP_OPC_AssociationType:
					ob->oi.AssociationType = prop->Value.u16;
					break;
				case PTP_OPC_AssociationDesc:
					ob->oi.AssociationDesc = prop->Value.u32;
					break;
				case PTP_OPC_ObjectFileName:
					if (prop->Value.str) {
						free(ob->oi.Filename);
						ob->oi.Filename = strdup(prop->Value.str);
					}
					break;
				case PTP_OPC_DateCreated:
					ob->oi.CaptureDate = ptp_unpack_PTPTIME(prop->Value.str);
					break;
				case PTP_OPC_DateModified:
					ob->oi.ModificationDate = ptp_unpack_PTPTIME(prop->Value.str);
					break;
				case PTP_OPC_Keywords:
					if (prop->Value.str) {
						free(ob->oi.Keywords);
						ob->oi.Keywords = strdup(prop->Value.str);
					}
					break;
				case PTP_OPC_ParentObject:
					ob->oi.ParentObject = prop->Value.u32;
					break;
				}
			}
		}
	}

	if ((ob->flags & want) != want) {
		ptp_debug (params, "ptp_object_want: handle 0x%08x, want flags %x, have only %x?", handle, want, ob->flags);
		return PTP_RC_GeneralError;
	}

	return PTP_RC_OK;
}


uint16_t
ptp_add_object_to_cache(PTPParams *params, uint32_t handle)
{
	PTPObject *ob;
	return ptp_object_want (params, handle, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
}


/*
 * Local Variables:
 * c-file-style:"linux"
 * indent-tabs-mode:t
 * End:
 */
