Move the libmtp source from jumper to external.

Change-Id: I06bce6628cf809af6f7125c8fb4115bafd7c6c87
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..bd10139
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,61 @@
+lib_LTLIBRARIES=libmtp.la
+libmtp_la_SOURCES=libmtp.c unicode.c unicode.h util.c util.h playlist-spl.c \
+	libusb-glue.c libusb-glue.h \
+	gphoto2-endian.h libptp-stdint.h ptp.c ptp.h \
+	music-players.h device-flags.h playlist-spl.h
+include_HEADERS=libmtp.h
+EXTRA_DIST=libmtp.h.in libmtp.sym ptp-pack.c
+
+# ---------------------------------------------------------------------------
+# Advanced information about versioning:
+#   * "Writing shared libraries" by Mike Hearn
+#         http://navi.cx/~mike/writing-shared-libraries.html
+#   * libtool.info chapter "Versioning"
+#   * libtool.info chapter "Updating library version information"
+# ---------------------------------------------------------------------------
+# Versioning:
+#  - CURRENT (Major):  Increment if the interface has changes. AGE is always
+#                      *changed* at the same time.
+#  - AGE (Micro):      Increment if any interfaces have been added; set to 0
+#		         if any interfaces have been removed. Removal has
+#                      precedence over adding, so set to 0 if both happened.
+#                      It denotes upward compatibility.
+#  - REVISION (Minor): Increment any time the source changes; set to
+#			 0 if you incremented CURRENT.
+#
+#  To summarize. Any interface *change* increment CURRENT. If that interface
+#  change does not break upward compatibility (ie it is an addition),
+#  increment AGE, Otherwise AGE is reset to 0. If CURRENT has changed,
+#  REVISION is set to 0, otherwise REVISION is incremented.
+# ---------------------------------------------------------------------------
+CURRENT=11
+AGE=3
+REVISION=1
+SOVERSION=$(CURRENT):$(REVISION):$(AGE)
+LT_CURRENT_MINUS_AGE=`expr $(CURRENT) - $(AGE)`
+
+if COMPILE_MINGW32
+W32_LIBS=-lws2_32
+W32_LDFLAGS=-export-dynamic
+if MS_LIB_EXE
+noinst_DATA=libmtp.lib
+libmtp.def: $(srcdir)/libmtp.sym
+	echo "LIBRARY \"@PACKAGE@\"" > libmtp.def
+	echo "DESCRIPTION \"Media Transfer Protocol (MTP) library\"" >> libmtp.def
+	echo "VERSION @VERSION@" >> libmtp.def
+	echo >> libmtp.def
+	echo "EXPORTS" >> libmtp.def
+	cat $< >> libmtp.def
+libmtp.lib: libmtp.la libmtp.def
+	lib -name:libmtp-$(LT_CURRENT_MINUS_AGE).dll -def:libmtp.def -out:$@
+install-data-local: libmtp.lib libmtp.def
+	$(INSTALL) libmtp.def $(DESTDIR)$(libdir)
+	$(INSTALL) libmtp.lib $(DESTDIR)$(libdir)
+endif
+endif
+
+libmtp_la_LDFLAGS=@LDFLAGS@ -no-undefined -export-symbols $(srcdir)/libmtp.sym -version-info $(SOVERSION) $(W32_LDFLAGS)
+libmtp_la_LIBADD=$(W32_LIBS) $(LTLIBICONV)
+libmtp_la_DEPENDENCIES=$(srcdir)/libmtp.sym
+
+DISTCLEANFILES = _stdint.h
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..3b294c0
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,616 @@
+# Makefile.in generated by automake 1.11 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = README $(include_HEADERS) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in $(srcdir)/libmtp.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/byteorder.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/stdint.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = libmtp.h
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am_libmtp_la_OBJECTS = libmtp.lo unicode.lo util.lo playlist-spl.lo \
+	libusb-glue.lo ptp.lo
+libmtp_la_OBJECTS = $(am_libmtp_la_OBJECTS)
+libmtp_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(libmtp_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libmtp_la_SOURCES)
+DIST_SOURCES = $(libmtp_la_SOURCES)
+DATA = $(noinst_DATA)
+HEADERS = $(include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+HAVE_DOXYGEN = @HAVE_DOXYGEN@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OSFLAGS = @OSFLAGS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+effective_target = @effective_target@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+ms_lib_exe = @ms_lib_exe@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LTLIBRARIES = libmtp.la
+libmtp_la_SOURCES = libmtp.c unicode.c unicode.h util.c util.h playlist-spl.c \
+	libusb-glue.c libusb-glue.h \
+	gphoto2-endian.h libptp-stdint.h ptp.c ptp.h \
+	music-players.h device-flags.h playlist-spl.h
+
+include_HEADERS = libmtp.h
+EXTRA_DIST = libmtp.h.in libmtp.sym ptp-pack.c
+
+# ---------------------------------------------------------------------------
+# Advanced information about versioning:
+#   * "Writing shared libraries" by Mike Hearn
+#         http://navi.cx/~mike/writing-shared-libraries.html
+#   * libtool.info chapter "Versioning"
+#   * libtool.info chapter "Updating library version information"
+# ---------------------------------------------------------------------------
+# Versioning:
+#  - CURRENT (Major):  Increment if the interface has changes. AGE is always
+#                      *changed* at the same time.
+#  - AGE (Micro):      Increment if any interfaces have been added; set to 0
+#		         if any interfaces have been removed. Removal has
+#                      precedence over adding, so set to 0 if both happened.
+#                      It denotes upward compatibility.
+#  - REVISION (Minor): Increment any time the source changes; set to
+#			 0 if you incremented CURRENT.
+#
+#  To summarize. Any interface *change* increment CURRENT. If that interface
+#  change does not break upward compatibility (ie it is an addition),
+#  increment AGE, Otherwise AGE is reset to 0. If CURRENT has changed,
+#  REVISION is set to 0, otherwise REVISION is incremented.
+# ---------------------------------------------------------------------------
+CURRENT = 11
+AGE = 3
+REVISION = 1
+SOVERSION = $(CURRENT):$(REVISION):$(AGE)
+LT_CURRENT_MINUS_AGE = `expr $(CURRENT) - $(AGE)`
+@COMPILE_MINGW32_TRUE@W32_LIBS = -lws2_32
+@COMPILE_MINGW32_TRUE@W32_LDFLAGS = -export-dynamic
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@noinst_DATA = libmtp.lib
+libmtp_la_LDFLAGS = @LDFLAGS@ -no-undefined -export-symbols $(srcdir)/libmtp.sym -version-info $(SOVERSION) $(W32_LDFLAGS)
+libmtp_la_LIBADD = $(W32_LIBS) $(LTLIBICONV)
+libmtp_la_DEPENDENCIES = $(srcdir)/libmtp.sym
+DISTCLEANFILES = _stdint.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+libmtp.h: $(top_builddir)/config.status $(srcdir)/libmtp.h.in
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libmtp.la: $(libmtp_la_OBJECTS) $(libmtp_la_DEPENDENCIES) 
+	$(libmtp_la_LINK) -rpath $(libdir) $(libmtp_la_OBJECTS) $(libmtp_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmtp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb-glue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/playlist-spl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unicode.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+@COMPILE_MINGW32_FALSE@install-data-local:
+@MS_LIB_EXE_FALSE@install-data-local:
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am \
+	install-data-local install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am \
+	install-includeHEADERS install-info install-info-am \
+	install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags uninstall uninstall-am uninstall-includeHEADERS \
+	uninstall-libLTLIBRARIES
+
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@libmtp.def: $(srcdir)/libmtp.sym
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	echo "LIBRARY \"@PACKAGE@\"" > libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	echo "DESCRIPTION \"Media Transfer Protocol (MTP) library\"" >> libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	echo "VERSION @VERSION@" >> libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	echo >> libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	echo "EXPORTS" >> libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	cat $< >> libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@libmtp.lib: libmtp.la libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	lib -name:libmtp-$(LT_CURRENT_MINUS_AGE).dll -def:libmtp.def -out:$@
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@install-data-local: libmtp.lib libmtp.def
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	$(INSTALL) libmtp.def $(DESTDIR)$(libdir)
+@COMPILE_MINGW32_TRUE@@MS_LIB_EXE_TRUE@	$(INSTALL) libmtp.lib $(DESTDIR)$(libdir)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/README b/src/README
new file mode 100644
index 0000000..d7e0f3a
--- /dev/null
+++ b/src/README
@@ -0,0 +1,25 @@
+RELATION TO LIBPTP2
+-------------------
+
+Parts of libptp2 are copied (and modified) from version 1.1.0:
+
+ptp-pack.c
+ptp-pack.h
+ptp.c
+ptp.h
+
+These are just copies of the same files from libptp2.
+In order to avoid clashes with the libptp2 endianness scripts
+we have named libptp-endian.h and libptp-stdint.h the
+same way as in libptp2, though they are created by libmtp
+autoconfigure scripts.
+
+We will try to track libptp2 and fold changes back into
+libmtp.
+
+TODO: check if the following still holds! (Linus)
+
+I have changed the config script slightly for the le64atoh function 
+in libptp-endian.h. This is required for OS X on PowerPC (not sure why). 
+I've just cast the bytes to uint64_t to avoid shifting wrongly.
+
diff --git a/src/device-flags.h b/src/device-flags.h
new file mode 100644
index 0000000..1b842c0
--- /dev/null
+++ b/src/device-flags.h
@@ -0,0 +1,247 @@
+/**
+ * \file device-flags.h
+ * Special device flags to deal with bugs in specific devices.
+ *
+ * Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2006-2007 Marcus Meissner
+ * Copyright (C) 2007 Ted Bullock
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * This file is supposed to be included by both libmtp and libgphoto2.
+ */
+
+/**
+ * These flags are used to indicate if some or other
+ * device need special treatment. These should be possible
+ * to concatenate using logical OR so please use one bit per
+ * feature and lets pray we don't need more than 32 bits...
+ */
+#define DEVICE_FLAG_NONE 0x00000000
+/**
+ * This means that the PTP_OC_MTP_GetObjPropList is broken
+ * in the sense that it won't return properly formatted metadata
+ * for ALL files on the device when you request an object 
+ * property list for object 0xFFFFFFFF with parameter 3 likewise
+ * set to 0xFFFFFFFF. Compare to 
+ * DEVICE_FLAG_BROKEN_MTPGETOBJECTPROPLIST which only signify
+ * that it's broken when getting metadata for a SINGLE object.
+ * A typical way the implementation may be broken is that it 
+ * may not return a proper count of the objects, and sometimes
+ * (like on the ZENs) objects are simply missing from the list
+ * if you use this. Sometimes it has been used incorrectly to
+ * mask bugs in the code (like handling transactions of data
+ * with size given to -1 (0xFFFFFFFFU), in that case please
+ * help us remove it now the code is fixed. Sometimes this is
+ * used because getting all the objects is just too slow and
+ * the USB transaction will time out if you use this command.
+ */
+#define DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL 0x00000001
+/**
+ * This means that under Linux, another kernel module may 
+ * be using this device's USB interface, so we need to detach 
+ * it if it is. Typically this is on dual-mode devices that
+ * will present both an MTP compliant interface and device
+ * descriptor *and* a USB mass storage interface. If the USB
+ * mass storage interface is in use, other apps (like our
+ * userspace libmtp through libusb access path) cannot get in
+ * and get cosy with it. So we can remove the offending 
+ * application. Typically this means you have to run the program
+ * as root as well.
+ */
+#define DEVICE_FLAG_UNLOAD_DRIVER 0x00000002
+/**
+ * This means that the PTP_OC_MTP_GetObjPropList is broken and
+ * won't properly return all object properties if parameter 3
+ * is set to 0xFFFFFFFFU.
+ */
+#define DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST 0x00000004
+/**
+ * This means the device doesn't send zero packets to indicate
+ * end of transfer when the transfer boundary occurs at a 
+ * multiple of 64 bytes (the USB 1.1 endpoint size). Instead, 
+ * exactly one extra byte is sent at the end of the transfer 
+ * if the size is an integer multiple of USB 1.1 endpoint size 
+ * (64 bytes).
+ *
+ * This behaviour is most probably a workaround due to the fact 
+ * that the hardware USB slave controller in the device cannot 
+ * handle zero writes at all, and the usage of the USB 1.1 
+ * endpoint size is due to the fact that the device will "gear 
+ * down" on a USB 1.1 hub, and since 64 bytes is a multiple of 
+ * 512 bytes, it will work with USB 1.1 and USB 2.0 alike.
+ */
+#define DEVICE_FLAG_NO_ZERO_READS 0x00000008
+/**
+ * This flag means that the device is prone to forgetting the
+ * OGG container file type, so that libmtp must look at the
+ * filename extensions in order to determine that a file is
+ * actually OGG. This is a clear and present firmware bug, and
+ * while firmware bugs should be fixed in firmware, we like
+ * OGG so much that we back it by introducing this flag.
+ * The error has only been seen on iriver devices. Turning this
+ * flag on won't hurt anything, just that the check against
+ * filename extension will be done for files of "unknown" type.
+ * If the player does not even know (reports) that it supports
+ * ogg even though it does, please use the stronger 
+ * OGG_IS_UNKNOWN flag, which will forcedly support ogg on
+ * anything with the .ogg filename extension.
+ */
+#define DEVICE_FLAG_IRIVER_OGG_ALZHEIMER 0x00000010
+/**
+ * This flag indicates a limitation in the filenames a device
+ * can accept - they must be 7 bit (all chars <= 127/0x7F).
+ * It was found first on the Philips Shoqbox, and is a deviation
+ * from the PTP standard which mandates that any unicode chars
+ * may be used for filenames. I guess this is caused by a 7bit-only
+ * filesystem being used intrinsically on the device.
+ */
+#define DEVICE_FLAG_ONLY_7BIT_FILENAMES 0x00000020
+/**
+ * This flag indicates that the device will lock up if you
+ * try to get status of endpoints and/or release the interface
+ * when closing the device. This fixes problems with SanDisk
+ * Sansa devices especially. It may be a side-effect of a
+ * Windows behaviour of never releasing interfaces.
+ */
+#define DEVICE_FLAG_NO_RELEASE_INTERFACE 0x00000040
+/**
+ * This flag was introduced with the advent of Creative ZEN
+ * 8GB. The device sometimes return a broken PTP header
+ * like this: < 1502 0000 0200 01d1 02d1 01d2 >
+ * the latter 6 bytes (representing "code" and "transaction ID")
+ * contain junk. This is breaking the PTP/MTP spec but works
+ * on Windows anyway, probably because the Windows implementation
+ * does not check that these bytes are valid. To interoperate
+ * with devices like this, we need this flag to emulate the 
+ * Windows bug.
+ */
+#define DEVICE_FLAG_IGNORE_HEADER_ERRORS 0x00000080
+/**
+ * The Motorola RAZR2 V8 (others?) has broken set object
+ * proplist causing the metadata setting to fail. (The
+ * set object prop to set individual properties work on
+ * this device, but the metadata is plain ignored on
+ * tracks, though e.g. playlist names can be set.)
+ */
+#define DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST 0x00000100
+/**
+ * The Samsung YP-T10 think Ogg files shall be sent with
+ * the "unknown" (PTP_OFC_Undefined) file type, this gives a 
+ * side effect that is a combination of the iRiver Ogg Alzheimer 
+ * problem (have to recognized Ogg files on file extension)
+ * and a need to report the Ogg support (the device itself does
+ * not properly claim to support it) and need to set filetype 
+ * to unknown when storing Ogg files, even though they're not
+ * actually unknown. Later iRivers seem to need this flag since
+ * they do not report to support OGG even though they actually
+ * do. Often the device supports OGG in USB mass storage mode,
+ * then the firmware simply miss to declare metadata support
+ * for OGG properly.
+ */
+#define DEVICE_FLAG_OGG_IS_UNKNOWN 0x00000200
+/**
+ * The Creative Zen is quite unstable in libmtp but seems to
+ * be better with later firmware versions. However, it still
+ * frequently crashes when setting album art dimensions. This
+ * flag disables setting the dimensions (which seems to make
+ * no difference to how the graphic is displayed).
+ */
+#define DEVICE_FLAG_BROKEN_SET_SAMPLE_DIMENSIONS 0x00000400
+/**
+ * Some devices, particularly SanDisk Sansas, need to always
+ * have their "OS Descriptor" probed in order to work correctly.
+ * This flag provides that extra massage.
+ */
+#define DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR 0x00000800
+/**
+ * Samsung has implimented its own playlist format as a .spl file
+ * stored in the normal file system, rather than a proper mtp
+ * playlist. There are multiple versions of the .spl format
+ * identified by a line in the file: VERSION X.XX
+ * Version 1.00 is just a simple playlist.
+ */
+#define DEVICE_FLAG_PLAYLIST_SPL_V1 0x00001000
+/**
+ * Samsung has implimented its own playlist format as a .spl file
+ * stored in the normal file system, rather than a proper mtp
+ * playlist. There are multiple versions of the .spl format
+ * identified by a line in the file: VERSION X.XX
+ * Version 2.00 is playlist but allows DNSe sound settings
+ * to be stored, per playlist.
+ */
+#define DEVICE_FLAG_PLAYLIST_SPL_V2 0x00002000
+/**
+ * The Sansa E250 is know to have this problem which is actually
+ * that the device claims that property PTP_OPC_DateModified
+ * is read/write but will still fail to update it. It can only
+ * be set properly the first time a file is sent.
+ */
+#define DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED 0x00004000
+/**
+ * This avoids use of the send object proplist which
+ * is used when creating new objects (not just updating)
+ * The DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST is related
+ * but only concerns the case where the object proplist
+ * is sent in to update an existing object. The Toshiba
+ * Gigabeat MEU202 for example has this problem.
+ */
+#define DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST 0x00008000
+/**
+ * Devices that cannot support reading out battery
+ * level.
+ */
+#define DEVICE_FLAG_BROKEN_BATTERY_LEVEL 0x00010000
+
+/**
+ * Devices that send "ObjectDeleted" events after deletion
+ * of images. (libgphoto2) 
+ */ 
+#define DEVICE_FLAG_DELETE_SENDS_EVENT	0x00020000
+
+/**
+ * Cameras that can capture images. (libgphoto2)
+ */ 
+#define DEVICE_FLAG_CAPTURE		0x00040000
+
+/**
+ * Cameras that can capture images. (libgphoto2)
+ */ 
+#define DEVICE_FLAG_CAPTURE_PREVIEW	0x00080000
+
+/**
+ * Nikon broken capture support without proper ObjectAdded events.
+ * (libgphoto2)
+ */ 
+#define DEVICE_FLAG_NIKON_BROKEN_CAPTURE	0x00100000
+
+/**
+ * Broken capture support where cameras do not send CaptureComplete events.
+ * (libgphoto2)
+ */ 
+#define DEVICE_FLAG_NO_CAPTURE_COMPLETE		0x00400000
+
+/**
+ * Direct PTP match required.
+ * (libgphoto2)
+ */ 
+#define DEVICE_FLAG_MATCH_PTP_INTERFACE		0x00800000
+/**
+ * This flag is like DEVICE_FLAG_OGG_IS_UNKNOWN but for FLAC
+ * files instead. Using the unknown filetype for FLAC files.
+ */
+#define DEVICE_FLAG_FLAC_IS_UNKNOWN 0x01000000
diff --git a/src/gphoto2-endian-intel.h b/src/gphoto2-endian-intel.h
new file mode 100644
index 0000000..89f84f6
--- /dev/null
+++ b/src/gphoto2-endian-intel.h
@@ -0,0 +1,137 @@
+/* This file is generated automatically by configure */
+/* It is valid only for the system type i386-apple-darwin10.4.0 */
+
+#ifndef __BYTEORDER_H
+#define __BYTEORDER_H
+
+/* ntohl and relatives live here */
+#include <arpa/inet.h>
+
+/* Define generic byte swapping functions */
+#include <machine/byte_order.h>
+#define swap16(x) NXSwapShort(x)
+#define swap32(x) NXSwapLong(x)
+#define swap64(x) NXSwapLongLong(x)
+
+/* The byte swapping macros have the form: */
+/*   EENN[a]toh or htoEENN[a] where EE is be (big endian) or */
+/* le (little-endian), NN is 16 or 32 (number of bits) and a, */
+/* if present, indicates that the endian side is a pointer to an */
+/* array of uint8_t bytes instead of an integer of the specified length. */
+/* h refers to the host's ordering method. */
+
+/* So, to convert a 32-bit integer stored in a buffer in little-endian */
+/* format into a uint32_t usable on this machine, you could use: */
+/*   uint32_t value = le32atoh(&buf[3]); */
+/* To put that value back into the buffer, you could use: */
+/*   htole32a(&buf[3], value); */
+
+/* Define aliases for the standard byte swapping macros */
+/* Arguments to these macros must be properly aligned on natural word */
+/* boundaries in order to work properly on all architectures */
+#ifndef htobe16
+# define htobe16(x) htons(x)
+#endif
+#ifndef htobe32
+# define htobe32(x) htonl(x)
+#endif
+#ifndef be16toh
+# define be16toh(x) ntohs(x)
+#endif
+#ifndef be32toh
+# define be32toh(x) ntohl(x)
+#endif
+
+#define HTOBE16(x) (x) = htobe16(x)
+#define HTOBE32(x) (x) = htobe32(x)
+#define BE32TOH(x) (x) = be32toh(x)
+#define BE16TOH(x) (x) = be16toh(x)
+
+/* On little endian machines, these macros are null */
+#ifndef htole16
+# define htole16(x)      (x)
+#endif
+#ifndef htole32
+# define htole32(x)      (x)
+#endif
+#ifndef htole64
+# define htole64(x)      (x)
+#endif
+#ifndef le16toh
+# define le16toh(x)      (x)
+#endif
+#ifndef le32toh
+# define le32toh(x)      (x)
+#endif
+#ifndef le64toh
+# define le64toh(x)      (x)
+#endif
+
+#define HTOLE16(x)      (void) (x)
+#define HTOLE32(x)      (void) (x)
+#define HTOLE64(x)      (void) (x)
+#define LE16TOH(x)      (void) (x)
+#define LE32TOH(x)      (void) (x)
+#define LE64TOH(x)      (void) (x)
+
+/* These don't have standard aliases */
+#ifndef htobe64
+# define htobe64(x)      swap64(x)
+#endif
+#ifndef be64toh
+# define be64toh(x)      swap64(x)
+#endif
+
+#define HTOBE64(x)      (x) = htobe64(x)
+#define BE64TOH(x)      (x) = be64toh(x)
+
+/* Define the C99 standard length-specific integer types */
+#include <stdint.h>
+
+/* Here are some macros to create integers from a byte array */
+/* These are used to get and put integers from/into a uint8_t array */
+/* with a specific endianness.  This is the most portable way to generate */
+/* and read messages to a network or serial device.  Each member of a */
+/* packet structure must be handled separately. */
+
+/* The i386 and compatibles can handle unaligned memory access, */
+/* so use the optimized macros above to do this job */
+#ifndef be16atoh
+# define be16atoh(x)     be16toh(*(uint16_t*)(x))
+#endif
+#ifndef be32atoh
+# define be32atoh(x)     be32toh(*(uint32_t*)(x))
+#endif
+#ifndef be64atoh
+# define be64atoh(x)     be64toh(*(uint64_t*)(x))
+#endif
+#ifndef le16atoh
+# define le16atoh(x)     le16toh(*(uint16_t*)(x))
+#endif
+#ifndef le32atoh
+# define le32atoh(x)     le32toh(*(uint32_t*)(x))
+#endif
+#ifndef le64atoh
+# define le64atoh(x)     le64toh(*(uint64_t*)(x))
+#endif
+
+#ifndef htob16a
+# define htobe16a(a,x)   *(uint16_t*)(a) = htobe16(x)
+#endif
+#ifndef htobe32a
+# define htobe32a(a,x)   *(uint32_t*)(a) = htobe32(x)
+#endif
+#ifndef htobe64a
+# define htobe64a(a,x)   *(uint64_t*)(a) = htobe64(x)
+#endif
+#ifndef htole16a
+# define htole16a(a,x)   *(uint16_t*)(a) = htole16(x)
+#endif
+#ifndef htole32a
+# define htole32a(a,x)   *(uint32_t*)(a) = htole32(x)
+#endif
+#ifndef htole64a
+# define htole64a(a,x)   *(uint64_t*)(a) = htole64(x)
+#endif
+
+#endif /*__BYTEORDER_H*/
diff --git a/src/gphoto2-endian-ppc.h b/src/gphoto2-endian-ppc.h
new file mode 100644
index 0000000..6ff07cc
--- /dev/null
+++ b/src/gphoto2-endian-ppc.h
@@ -0,0 +1,119 @@
+/* This file is generated automatically by configure */
+/* It is valid only for the system type powerpc-apple-darwin9.8.0 */
+
+#ifndef __BYTEORDER_H
+#define __BYTEORDER_H
+
+/* ntohl and relatives live here */
+#include <arpa/inet.h>
+
+/* Define generic byte swapping functions */
+#include <machine/byte_order.h>
+#define swap16(x) NXSwapShort(x)
+#define swap32(x) NXSwapLong(x)
+#define swap64(x) NXSwapLongLong(x)
+
+/* The byte swapping macros have the form: */
+/*   EENN[a]toh or htoEENN[a] where EE is be (big endian) or */
+/* le (little-endian), NN is 16 or 32 (number of bits) and a, */
+/* if present, indicates that the endian side is a pointer to an */
+/* array of uint8_t bytes instead of an integer of the specified length. */
+/* h refers to the host's ordering method. */
+
+/* So, to convert a 32-bit integer stored in a buffer in little-endian */
+/* format into a uint32_t usable on this machine, you could use: */
+/*   uint32_t value = le32atoh(&buf[3]); */
+/* To put that value back into the buffer, you could use: */
+/*   htole32a(&buf[3], value); */
+
+/* Define aliases for the standard byte swapping macros */
+/* Arguments to these macros must be properly aligned on natural word */
+/* boundaries in order to work properly on all architectures */
+#ifndef htobe16
+# define htobe16(x) htons(x)
+#endif
+#ifndef htobe32
+# define htobe32(x) htonl(x)
+#endif
+#ifndef be16toh
+# define be16toh(x) ntohs(x)
+#endif
+#ifndef be32toh
+# define be32toh(x) ntohl(x)
+#endif
+
+#define HTOBE16(x) (x) = htobe16(x)
+#define HTOBE32(x) (x) = htobe32(x)
+#define BE32TOH(x) (x) = be32toh(x)
+#define BE16TOH(x) (x) = be16toh(x)
+
+/* Define our own extended byte swapping macros for big-endian machines */
+#ifndef htole16
+# define htole16(x)      swap16(x)
+#endif
+#ifndef htole32
+# define htole32(x)      swap32(x)
+#endif
+#ifndef le16toh
+# define le16toh(x)      swap16(x)
+#endif
+#ifndef le32toh
+# define le32toh(x)      swap32(x)
+#endif
+#ifndef le64toh
+# define le64toh(x)      swap64(x)
+#endif
+
+#ifndef htobe64
+# define htobe64(x)      (x)
+#endif
+#ifndef be64toh
+# define be64toh(x)      (x)
+#endif
+
+#define HTOLE16(x)      (x) = htole16(x)
+#define HTOLE32(x)      (x) = htole32(x)
+#define LE16TOH(x)      (x) = le16toh(x)
+#define LE32TOH(x)      (x) = le32toh(x)
+#define LE64TOH(x)      (x) = le64toh(x)
+
+#define HTOBE64(x)      (void) (x)
+#define BE64TOH(x)      (void) (x)
+
+/* Define the C99 standard length-specific integer types */
+#include <stdint.h>
+
+/* Here are some macros to create integers from a byte array */
+/* These are used to get and put integers from/into a uint8_t array */
+/* with a specific endianness.  This is the most portable way to generate */
+/* and read messages to a network or serial device.  Each member of a */
+/* packet structure must be handled separately. */
+
+/* Non-optimized but portable macros */
+#define be16atoh(x)     ((uint16_t)(((x)[0]<<8)|(x)[1]))
+#define be32atoh(x)     ((uint32_t)(((x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]))
+#define be64atoh_x(x,off,shift) 	(((uint64_t)((x)[off]))<<shift)
+#define be64atoh(x)     ((uint64_t)(be64atoh_x(x,0,56)|be64atoh_x(x,1,48)|be64atoh_x(x,2,40)| \
+        be64atoh_x(x,3,32)|be64atoh_x(x,4,24)|be64atoh_x(x,5,16)|be64atoh_x(x,6,8)|((x)[7])))
+#define le16atoh(x)     ((uint16_t)(((x)[1]<<8)|(x)[0]))
+#define le32atoh(x)     ((uint32_t)(((x)[3]<<24)|((x)[2]<<16)|((x)[1]<<8)|(x)[0]))
+#define le64atoh_x(x,off,shift) (((uint64_t)(x)[off])<<shift)
+#define le64atoh(x)     ((uint64_t)(le64atoh_x(x,7,56)|le64atoh_x(x,6,48)|le64atoh_x(x,5,40)| \
+        le64atoh_x(x,4,32)|le64atoh_x(x,3,24)|le64atoh_x(x,2,16)|le64atoh_x(x,1,8)|((x)[0])))
+
+#define htobe16a(a,x)   (a)[0]=(uint8_t)((x)>>8), (a)[1]=(uint8_t)(x)
+#define htobe32a(a,x)   (a)[0]=(uint8_t)((x)>>24), (a)[1]=(uint8_t)((x)>>16), \
+        (a)[2]=(uint8_t)((x)>>8), (a)[3]=(uint8_t)(x)
+#define htobe64a(a,x)   (a)[0]=(uint8_t)((x)>>56), (a)[1]=(uint8_t)((x)>>48), \
+        (a)[2]=(uint8_t)((x)>>40), (a)[3]=(uint8_t)((x)>>32), \
+        (a)[4]=(uint8_t)((x)>>24), (a)[5]=(uint8_t)((x)>>16), \
+        (a)[6]=(uint8_t)((x)>>8), (a)[7]=(uint8_t)(x)
+#define htole16a(a,x)   (a)[1]=(uint8_t)((x)>>8), (a)[0]=(uint8_t)(x)
+#define htole32a(a,x)   (a)[3]=(uint8_t)((x)>>24), (a)[2]=(uint8_t)((x)>>16), \
+        (a)[1]=(uint8_t)((x)>>8), (a)[0]=(uint8_t)(x)
+#define htole64a(a,x)   (a)[7]=(uint8_t)((x)>>56), (a)[6]=(uint8_t)((x)>>48), \
+        (a)[5]=(uint8_t)((x)>>40), (a)[4]=(uint8_t)((x)>>32), \
+        (a)[3]=(uint8_t)((x)>>24), (a)[2]=(uint8_t)((x)>>16), \
+        (a)[1]=(uint8_t)((x)>>8), (a)[0]=(uint8_t)(x)
+
+#endif /*__BYTEORDER_H*/
diff --git a/src/gphoto2-endian.h b/src/gphoto2-endian.h
new file mode 100644
index 0000000..90cdeae
--- /dev/null
+++ b/src/gphoto2-endian.h
@@ -0,0 +1,7 @@
+/* added 10/26/2010 to deal with both archs on Mac */
+
+#ifdef __BIG_ENDIAN__
+#include "gphoto2-endian-ppc.h"
+#else
+#include "gphoto2-endian-intel.h"
+#endif
diff --git a/src/libmtp.c b/src/libmtp.c
new file mode 100644
index 0000000..90bdb04
--- /dev/null
+++ b/src/libmtp.c
@@ -0,0 +1,8372 @@
+/**
+ * \file libmtp.c
+ *
+ * Copyright (C) 2005-2009 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
+ * Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
+ * Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * This file provides an interface "glue" to the underlying
+ * PTP implementation from libgphoto2. It uses some local
+ * code to convert from/to UTF-8 (stored in unicode.c/.h)
+ * and some small utility functions, mainly for debugging
+ * (stored in util.c/.h).
+ *
+ * The three PTP files (ptp.c, ptp.h and ptp-pack.c) are
+ * plain copied from the libhphoto2 codebase.
+ *
+ * The files libusb-glue.c/.h are just what they say: an
+ * interface to libusb for the actual, physical USB traffic.
+ */
+#include "config.h"
+#include "libmtp.h"
+#include "unicode.h"
+#include "ptp.h"
+#include "libusb-glue.h"
+#include "device-flags.h"
+#include "playlist-spl.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#ifdef _MSC_VER // For MSVC++
+#define USE_WINDOWS_IO_H
+#include <io.h>
+#endif
+
+/* To enable PTP level debug prints (all ptp_debug(...)), switch on this */
+//#define ENABLE_PTP_DEBUG
+
+/*
+ * This is a mapping between libmtp internal MTP filetypes and
+ * the libgphoto2/PTP equivalent defines. We need this because
+ * otherwise the libmtp.h device has to be dependent on ptp.h
+ * to be installed too, and we don't want that.
+ */
+//typedef struct filemap_struct filemap_t;
+typedef struct filemap_struct {
+  char *description; /**< Text description for the file type */
+  LIBMTP_filetype_t id; /**< LIBMTP internal type for the file type */
+  uint16_t ptp_id; /**< PTP ID for the filetype */
+  struct filemap_struct *next;
+} filemap_t;
+
+/*
+ * This is a mapping between libmtp internal MTP properties and
+ * the libgphoto2/PTP equivalent defines. We need this because
+ * otherwise the libmtp.h device has to be dependent on ptp.h
+ * to be installed too, and we don't want that.
+ */
+typedef struct propertymap_struct {
+  char *description; /**< Text description for the property */
+  LIBMTP_property_t id; /**< LIBMTP internal type for the property */
+  uint16_t ptp_id; /**< PTP ID for the property */
+  struct propertymap_struct *next;
+} propertymap_t;
+
+// Global variables
+// This holds the global filetype mapping table
+static filemap_t *filemap = NULL;
+// This holds the global property mapping table
+static propertymap_t *propertymap = NULL;
+
+static int load_cache_on_demand = 0;
+/*
+ * Forward declarations of local (static) functions.
+ */
+static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
+			     uint16_t const ptp_id);
+static void init_filemap();
+static int register_property(char const * const description, LIBMTP_property_t const id,
+			     uint16_t const ptp_id);
+static void init_propertymap();
+static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
+				    LIBMTP_error_number_t errornumber,
+				    char const * const error_text);
+static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
+					uint16_t ptp_error,
+					char const * const error_text);
+static void flush_handles(LIBMTP_mtpdevice_t *device);
+static void get_handles_recursively(LIBMTP_mtpdevice_t *device, 
+				    PTPParams *params, 
+				    uint32_t storageid,
+				    uint32_t parent);
+static void free_storage_list(LIBMTP_mtpdevice_t *device);
+static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby);
+static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fitsize);
+static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
+				 LIBMTP_devicestorage_t *storage,
+				 uint64_t *freespace);
+static int check_if_file_fits(LIBMTP_mtpdevice_t *device, 
+			      LIBMTP_devicestorage_t *storage,
+			      uint64_t const filesize);
+static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype);
+static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype);
+static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty);
+static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t intype);
+static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
+				       char **unicstring, uint16_t property);
+static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd);
+static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd);
+static char *get_iso8601_stamp(void);
+static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    uint16_t const attribute_id);
+static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+                                    uint16_t const attribute_id, uint64_t const value_default);
+static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+				    uint16_t const attribute_id, uint32_t const value_default);
+static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    uint16_t const attribute_id, uint16_t const value_default);
+static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				  uint16_t const attribute_id, uint8_t const value_default);
+static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			     uint16_t const attribute_id, char const * const string);
+static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  uint16_t const attribute_id, uint32_t const value);
+static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  uint16_t const attribute_id, uint16_t const value);
+static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			 uint16_t const attribute_id, uint8_t const value);
+static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
+			       LIBMTP_track_t *track);
+static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent);
+static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
+				    char const * const name,
+				    char const * const artist,
+				    char const * const composer,
+				    char const * const genre,
+				    uint32_t const parenthandle,
+				    uint32_t const storageid,
+				    uint16_t const objectformat,
+				    char const * const suffix,
+				    uint32_t * const newid,
+				    uint32_t const * const tracks,
+				    uint32_t const no_tracks);
+static int update_abstract_list(LIBMTP_mtpdevice_t *device,
+				char const * const name,
+				char const * const artist,
+				char const * const composer,
+				char const * const genre,
+				uint32_t const objecthandle,
+				uint16_t const objectformat,
+				uint32_t const * const tracks,
+				uint32_t const no_tracks);
+static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata);
+static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
+static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id);
+static int set_object_filename(LIBMTP_mtpdevice_t *device,
+		uint32_t object_id,
+		uint16_t ptp_type,
+                const char **newname);
+                
+/**
+ * These are to wrap the get/put handlers to convert from the MTP types to PTP types
+ * in a reliable way
+ */
+typedef struct _MTPDataHandler {
+	MTPDataGetFunc		getfunc;
+	MTPDataPutFunc		putfunc;
+	void			*priv;
+} MTPDataHandler;
+
+static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen);
+static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen);
+                
+/**
+ * Checks if a filename ends with ".ogg". Used in various
+ * situations when the device has no idea that it support
+ * OGG but still does.
+ *
+ * @param name string to be checked.
+ * @return 0 if this does not end with ogg, any other
+ *           value means it does.
+ */
+static int has_ogg_extension(char *name) {
+  char *ptype;
+
+  if (name == NULL)
+    return 0;
+  ptype = strrchr(name,'.');
+  if (ptype == NULL)
+    return 0;
+  if (!strcasecmp (ptype, ".ogg"))
+    return 1;
+  return 0;
+}
+
+/**
+ * Checks if a filename ends with ".flac". Used in various
+ * situations when the device has no idea that it support
+ * FLAC but still does.
+ *
+ * @param name string to be checked.
+ * @return 0 if this does not end with flac, any other
+ *           value means it does.
+ */
+static int has_flac_extension(char *name) {
+  char *ptype;
+
+  if (name == NULL)
+    return 0;
+  ptype = strrchr(name,'.');
+  if (ptype == NULL)
+    return 0;
+  if (!strcasecmp (ptype, ".flac"))
+    return 1;
+  return 0;
+}
+
+
+
+/**
+ * Create a new file mapping entry
+ * @return a newly allocated filemapping entry.
+ */
+static filemap_t *new_filemap_entry()
+{
+  filemap_t *filemap;
+
+  filemap = (filemap_t *)malloc(sizeof(filemap_t));
+
+  if( filemap != NULL ) {
+    filemap->description = NULL;
+    filemap->id = LIBMTP_FILETYPE_UNKNOWN;
+    filemap->ptp_id = PTP_OFC_Undefined;
+    filemap->next = NULL;
+  }
+
+  return filemap;
+}
+
+/**
+ * Register an MTP or PTP filetype for data retrieval
+ *
+ * @param description Text description of filetype
+ * @param id libmtp internal filetype id
+ * @param ptp_id PTP filetype id
+ * @return 0 for success any other value means error.
+*/
+static int register_filetype(char const * const description, LIBMTP_filetype_t const id,
+			     uint16_t const ptp_id)
+{
+  filemap_t *new = NULL, *current;
+
+  // Has this LIBMTP filetype been registered before ?
+  current = filemap;
+  while (current != NULL) {
+    if(current->id == id) {
+      break;
+    }
+    current = current->next;
+  }
+
+  // Create the entry
+  if(current == NULL) {
+    new = new_filemap_entry();
+    if(new == NULL) {
+      return 1;
+    }
+
+    new->id = id;
+    if(description != NULL) {
+      new->description = strdup(description);
+    }
+    new->ptp_id = ptp_id;
+
+    // Add the entry to the list
+    if(filemap == NULL) {
+      filemap = new;
+    } else {
+      current = filemap;
+      while (current->next != NULL ) current=current->next;
+      current->next = new;
+    }
+    // Update the existing entry
+  } else {
+    if (current->description != NULL) {
+      free(current->description);
+    }
+    current->description = NULL;
+    if(description != NULL) {
+      current->description = strdup(description);
+    }
+    current->ptp_id = ptp_id;
+  }
+
+  return 0;
+}
+
+static void init_filemap()
+{
+  register_filetype("Folder", LIBMTP_FILETYPE_FOLDER, PTP_OFC_Association);
+  register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
+  register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
+  register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
+  register_filetype("ISO MPEG-1 Audio Layer 2", LIBMTP_FILETYPE_MP2, PTP_OFC_MTP_MP2);
+  register_filetype("Microsoft Windows Media Audio", LIBMTP_FILETYPE_WMA, PTP_OFC_MTP_WMA);
+  register_filetype("Ogg container format", LIBMTP_FILETYPE_OGG, PTP_OFC_MTP_OGG);
+  register_filetype("Free Lossless Audio Codec (FLAC)", LIBMTP_FILETYPE_FLAC, PTP_OFC_MTP_FLAC);
+  register_filetype("Advanced Audio Coding (AAC)/MPEG-2 Part 7/MPEG-4 Part 3", LIBMTP_FILETYPE_AAC, PTP_OFC_MTP_AAC);
+  register_filetype("MPEG-4 Part 14 Container Format (Audio Emphasis)", LIBMTP_FILETYPE_M4A, PTP_OFC_MTP_M4A);
+  register_filetype("MPEG-4 Part 14 Container Format (Audio+Video Emphasis)", LIBMTP_FILETYPE_MP4, PTP_OFC_MTP_MP4);
+  register_filetype("Audible.com Audio Codec", LIBMTP_FILETYPE_AUDIBLE, PTP_OFC_MTP_AudibleCodec);
+  register_filetype("Undefined audio file", LIBMTP_FILETYPE_UNDEF_AUDIO, PTP_OFC_MTP_UndefinedAudio);
+  register_filetype("Microsoft Windows Media Video", LIBMTP_FILETYPE_WMV, PTP_OFC_MTP_WMV);
+  register_filetype("Audio Video Interleave", LIBMTP_FILETYPE_AVI, PTP_OFC_AVI);
+  register_filetype("MPEG video stream", LIBMTP_FILETYPE_MPEG, PTP_OFC_MPEG);
+  register_filetype("Microsoft Advanced Systems Format", LIBMTP_FILETYPE_ASF, PTP_OFC_ASF);
+  register_filetype("Apple Quicktime container format", LIBMTP_FILETYPE_QT, PTP_OFC_QT);
+  register_filetype("Undefined video file", LIBMTP_FILETYPE_UNDEF_VIDEO, PTP_OFC_MTP_UndefinedVideo);
+  register_filetype("JPEG file", LIBMTP_FILETYPE_JPEG, PTP_OFC_EXIF_JPEG);
+  register_filetype("JP2 file", LIBMTP_FILETYPE_JP2, PTP_OFC_JP2);
+  register_filetype("JPX file", LIBMTP_FILETYPE_JPX, PTP_OFC_JPX);
+  register_filetype("JFIF file", LIBMTP_FILETYPE_JFIF, PTP_OFC_JFIF);
+  register_filetype("TIFF bitmap file", LIBMTP_FILETYPE_TIFF, PTP_OFC_TIFF);
+  register_filetype("BMP bitmap file", LIBMTP_FILETYPE_BMP, PTP_OFC_BMP);
+  register_filetype("GIF bitmap file", LIBMTP_FILETYPE_GIF, PTP_OFC_GIF);
+  register_filetype("PICT bitmap file", LIBMTP_FILETYPE_PICT, PTP_OFC_PICT);
+  register_filetype("Portable Network Graphics", LIBMTP_FILETYPE_PNG, PTP_OFC_PNG);
+  register_filetype("Microsoft Windows Image Format", LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT, PTP_OFC_MTP_WindowsImageFormat);
+  register_filetype("VCalendar version 1", LIBMTP_FILETYPE_VCALENDAR1, PTP_OFC_MTP_vCalendar1);
+  register_filetype("VCalendar version 2", LIBMTP_FILETYPE_VCALENDAR2, PTP_OFC_MTP_vCalendar2);
+  register_filetype("VCard version 2", LIBMTP_FILETYPE_VCARD2, PTP_OFC_MTP_vCard2);
+  register_filetype("VCard version 3", LIBMTP_FILETYPE_VCARD3, PTP_OFC_MTP_vCard3);
+  register_filetype("Undefined Windows executable file", LIBMTP_FILETYPE_WINEXEC, PTP_OFC_MTP_UndefinedWindowsExecutable);
+  register_filetype("Text file", LIBMTP_FILETYPE_TEXT, PTP_OFC_Text);
+  register_filetype("HTML file", LIBMTP_FILETYPE_HTML, PTP_OFC_HTML);
+  register_filetype("XML file", LIBMTP_FILETYPE_XML, PTP_OFC_MTP_XMLDocument);
+  register_filetype("DOC file", LIBMTP_FILETYPE_DOC, PTP_OFC_MTP_MSWordDocument);
+  register_filetype("XLS file", LIBMTP_FILETYPE_XLS, PTP_OFC_MTP_MSExcelSpreadsheetXLS);
+  register_filetype("PPT file", LIBMTP_FILETYPE_PPT, PTP_OFC_MTP_MSPowerpointPresentationPPT);
+  register_filetype("MHT file", LIBMTP_FILETYPE_MHT, PTP_OFC_MTP_MHTCompiledHTMLDocument);
+  register_filetype("Firmware file", LIBMTP_FILETYPE_FIRMWARE, PTP_OFC_MTP_Firmware);
+  register_filetype("Abstract Album file", LIBMTP_FILETYPE_ALBUM, PTP_OFC_MTP_AbstractAudioAlbum);
+  register_filetype("Abstract Playlist file", LIBMTP_FILETYPE_PLAYLIST, PTP_OFC_MTP_AbstractAudioVideoPlaylist);
+  register_filetype("Undefined filetype", LIBMTP_FILETYPE_UNKNOWN, PTP_OFC_Undefined);
+}
+
+/**
+ * Returns the PTP filetype that maps to a certain libmtp internal file type.
+ * @param intype the MTP library interface type
+ * @return the PTP (libgphoto2) interface type
+ */
+static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
+{
+  filemap_t *current;
+
+  current = filemap;
+
+  while (current != NULL) {
+    if(current->id == intype) {
+      return current->ptp_id;
+    }
+    current = current->next;
+  }
+  // printf("map_libmtp_type_to_ptp_type: unknown filetype.\n");
+  return PTP_OFC_Undefined;
+}
+
+
+/**
+ * Returns the MTP internal interface type that maps to a certain ptp
+ * interface type.
+ * @param intype the PTP (libgphoto2) interface type
+ * @return the MTP library interface type
+ */
+static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
+{
+  filemap_t *current;
+
+  current = filemap;
+
+  while (current != NULL) {
+    if(current->ptp_id == intype) {
+      return current->id;
+    }
+    current = current->next;
+  }
+  // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
+  return LIBMTP_FILETYPE_UNKNOWN;
+}
+
+/**
+ * Create a new property mapping entry
+ * @return a newly allocated propertymapping entry.
+ */
+static propertymap_t *new_propertymap_entry()
+{
+  propertymap_t *propertymap;
+
+  propertymap = (propertymap_t *)malloc(sizeof(propertymap_t));
+
+  if( propertymap != NULL ) {
+    propertymap->description = NULL;
+    propertymap->id = LIBMTP_PROPERTY_UNKNOWN;
+    propertymap->ptp_id = 0;
+    propertymap->next = NULL;
+  }
+
+  return propertymap;
+}
+
+/**
+ * Register an MTP or PTP property for data retrieval
+ *
+ * @param description Text description of property
+ * @param id libmtp internal property id
+ * @param ptp_id PTP property id
+ * @return 0 for success any other value means error.
+*/
+static int register_property(char const * const description, LIBMTP_property_t const id,
+			     uint16_t const ptp_id)
+{
+  propertymap_t *new = NULL, *current;
+
+  // Has this LIBMTP propety been registered before ?
+  current = propertymap;
+  while (current != NULL) {
+    if(current->id == id) {
+      break;
+    }
+    current = current->next;
+  }
+
+  // Create the entry
+  if(current == NULL) {
+    new = new_propertymap_entry();
+    if(new == NULL) {
+      return 1;
+    }
+
+    new->id = id;
+    if(description != NULL) {
+      new->description = strdup(description);
+    }
+    new->ptp_id = ptp_id;
+
+    // Add the entry to the list
+    if(propertymap == NULL) {
+      propertymap = new;
+    } else {
+      current = propertymap;
+      while (current->next != NULL ) current=current->next;
+      current->next = new;
+    }
+    // Update the existing entry
+  } else {
+    if (current->description != NULL) {
+      free(current->description);
+    }
+    current->description = NULL;
+    if(description != NULL) {
+      current->description = strdup(description);
+    }
+    current->ptp_id = ptp_id;
+  }
+
+  return 0;
+}
+
+static void init_propertymap()
+{
+  register_property("Storage ID", LIBMTP_PROPERTY_StorageID, PTP_OPC_StorageID);
+  register_property("Object Format", LIBMTP_PROPERTY_ObjectFormat, PTP_OPC_ObjectFormat);
+  register_property("Protection Status", LIBMTP_PROPERTY_ProtectionStatus, PTP_OPC_ProtectionStatus);
+  register_property("Object Size", LIBMTP_PROPERTY_ObjectSize, PTP_OPC_ObjectSize);
+  register_property("Association Type", LIBMTP_PROPERTY_AssociationType, PTP_OPC_AssociationType);
+  register_property("Association Desc", LIBMTP_PROPERTY_AssociationDesc, PTP_OPC_AssociationDesc);
+  register_property("Object File Name", LIBMTP_PROPERTY_ObjectFileName, PTP_OPC_ObjectFileName);
+  register_property("Date Created", LIBMTP_PROPERTY_DateCreated, PTP_OPC_DateCreated);
+  register_property("Date Modified", LIBMTP_PROPERTY_DateModified, PTP_OPC_DateModified);
+  register_property("Keywords", LIBMTP_PROPERTY_Keywords, PTP_OPC_Keywords);
+  register_property("Parent Object", LIBMTP_PROPERTY_ParentObject, PTP_OPC_ParentObject);
+  register_property("Allowed Folder Contents", LIBMTP_PROPERTY_AllowedFolderContents, PTP_OPC_AllowedFolderContents);
+  register_property("Hidden", LIBMTP_PROPERTY_Hidden, PTP_OPC_Hidden);
+  register_property("System Object", LIBMTP_PROPERTY_SystemObject, PTP_OPC_SystemObject);
+  register_property("Persistant Unique Object Identifier", LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier, PTP_OPC_PersistantUniqueObjectIdentifier);
+  register_property("Sync ID", LIBMTP_PROPERTY_SyncID, PTP_OPC_SyncID);
+  register_property("Property Bag", LIBMTP_PROPERTY_PropertyBag, PTP_OPC_PropertyBag);
+  register_property("Name", LIBMTP_PROPERTY_Name, PTP_OPC_Name);
+  register_property("Created By", LIBMTP_PROPERTY_CreatedBy, PTP_OPC_CreatedBy);
+  register_property("Artist", LIBMTP_PROPERTY_Artist, PTP_OPC_Artist);
+  register_property("Date Authored", LIBMTP_PROPERTY_DateAuthored, PTP_OPC_DateAuthored);
+  register_property("Description", LIBMTP_PROPERTY_Description, PTP_OPC_Description);
+  register_property("URL Reference", LIBMTP_PROPERTY_URLReference, PTP_OPC_URLReference);
+  register_property("Language Locale", LIBMTP_PROPERTY_LanguageLocale, PTP_OPC_LanguageLocale);
+  register_property("Copyright Information", LIBMTP_PROPERTY_CopyrightInformation, PTP_OPC_CopyrightInformation);
+  register_property("Source", LIBMTP_PROPERTY_Source, PTP_OPC_Source);
+  register_property("Origin Location", LIBMTP_PROPERTY_OriginLocation, PTP_OPC_OriginLocation);
+  register_property("Date Added", LIBMTP_PROPERTY_DateAdded, PTP_OPC_DateAdded);
+  register_property("Non Consumable", LIBMTP_PROPERTY_NonConsumable, PTP_OPC_NonConsumable);
+  register_property("Corrupt Or Unplayable", LIBMTP_PROPERTY_CorruptOrUnplayable, PTP_OPC_CorruptOrUnplayable);
+  register_property("Producer Serial Number", LIBMTP_PROPERTY_ProducerSerialNumber, PTP_OPC_ProducerSerialNumber);
+  register_property("Representative Sample Format", LIBMTP_PROPERTY_RepresentativeSampleFormat, PTP_OPC_RepresentativeSampleFormat);
+  register_property("Representative Sample Sise", LIBMTP_PROPERTY_RepresentativeSampleSize, PTP_OPC_RepresentativeSampleSize);
+  register_property("Representative Sample Height", LIBMTP_PROPERTY_RepresentativeSampleHeight, PTP_OPC_RepresentativeSampleHeight);
+  register_property("Representative Sample Width", LIBMTP_PROPERTY_RepresentativeSampleWidth, PTP_OPC_RepresentativeSampleWidth);
+  register_property("Representative Sample Duration", LIBMTP_PROPERTY_RepresentativeSampleDuration, PTP_OPC_RepresentativeSampleDuration);
+  register_property("Representative Sample Data", LIBMTP_PROPERTY_RepresentativeSampleData, PTP_OPC_RepresentativeSampleData);
+  register_property("Width", LIBMTP_PROPERTY_Width, PTP_OPC_Width);
+  register_property("Height", LIBMTP_PROPERTY_Height, PTP_OPC_Height);
+  register_property("Duration", LIBMTP_PROPERTY_Duration, PTP_OPC_Duration);
+  register_property("Rating", LIBMTP_PROPERTY_Rating, PTP_OPC_Rating);
+  register_property("Track", LIBMTP_PROPERTY_Track, PTP_OPC_Track);
+  register_property("Genre", LIBMTP_PROPERTY_Genre, PTP_OPC_Genre);
+  register_property("Credits", LIBMTP_PROPERTY_Credits, PTP_OPC_Credits);
+  register_property("Lyrics", LIBMTP_PROPERTY_Lyrics, PTP_OPC_Lyrics);
+  register_property("Subscription Content ID", LIBMTP_PROPERTY_SubscriptionContentID, PTP_OPC_SubscriptionContentID);
+  register_property("Produced By", LIBMTP_PROPERTY_ProducedBy, PTP_OPC_ProducedBy);
+  register_property("Use Count", LIBMTP_PROPERTY_UseCount, PTP_OPC_UseCount);
+  register_property("Skip Count", LIBMTP_PROPERTY_SkipCount, PTP_OPC_SkipCount);
+  register_property("Last Accessed", LIBMTP_PROPERTY_LastAccessed, PTP_OPC_LastAccessed);
+  register_property("Parental Rating", LIBMTP_PROPERTY_ParentalRating, PTP_OPC_ParentalRating);
+  register_property("Meta Genre", LIBMTP_PROPERTY_MetaGenre, PTP_OPC_MetaGenre);
+  register_property("Composer", LIBMTP_PROPERTY_Composer, PTP_OPC_Composer);
+  register_property("Effective Rating", LIBMTP_PROPERTY_EffectiveRating, PTP_OPC_EffectiveRating);
+  register_property("Subtitle", LIBMTP_PROPERTY_Subtitle, PTP_OPC_Subtitle);
+  register_property("Original Release Date", LIBMTP_PROPERTY_OriginalReleaseDate, PTP_OPC_OriginalReleaseDate);
+  register_property("Album Name", LIBMTP_PROPERTY_AlbumName, PTP_OPC_AlbumName);
+  register_property("Album Artist", LIBMTP_PROPERTY_AlbumArtist, PTP_OPC_AlbumArtist);
+  register_property("Mood", LIBMTP_PROPERTY_Mood, PTP_OPC_Mood);
+  register_property("DRM Status", LIBMTP_PROPERTY_DRMStatus, PTP_OPC_DRMStatus);
+  register_property("Sub Description", LIBMTP_PROPERTY_SubDescription, PTP_OPC_SubDescription);
+  register_property("Is Cropped", LIBMTP_PROPERTY_IsCropped, PTP_OPC_IsCropped);
+  register_property("Is Color Corrected", LIBMTP_PROPERTY_IsColorCorrected, PTP_OPC_IsColorCorrected);
+  register_property("Image Bit Depth", LIBMTP_PROPERTY_ImageBitDepth, PTP_OPC_ImageBitDepth);
+  register_property("f Number", LIBMTP_PROPERTY_Fnumber, PTP_OPC_Fnumber);
+  register_property("Exposure Time", LIBMTP_PROPERTY_ExposureTime, PTP_OPC_ExposureTime);
+  register_property("Exposure Index", LIBMTP_PROPERTY_ExposureIndex, PTP_OPC_ExposureIndex);
+  register_property("Display Name", LIBMTP_PROPERTY_DisplayName, PTP_OPC_DisplayName);
+  register_property("Body Text", LIBMTP_PROPERTY_BodyText, PTP_OPC_BodyText);
+  register_property("Subject", LIBMTP_PROPERTY_Subject, PTP_OPC_Subject);
+  register_property("Priority", LIBMTP_PROPERTY_Priority, PTP_OPC_Priority);
+  register_property("Given Name", LIBMTP_PROPERTY_GivenName, PTP_OPC_GivenName);
+  register_property("Middle Names", LIBMTP_PROPERTY_MiddleNames, PTP_OPC_MiddleNames);
+  register_property("Family Name", LIBMTP_PROPERTY_FamilyName, PTP_OPC_FamilyName);
+  register_property("Prefix", LIBMTP_PROPERTY_Prefix, PTP_OPC_Prefix);
+  register_property("Suffix", LIBMTP_PROPERTY_Suffix, PTP_OPC_Suffix);
+  register_property("Phonetic Given Name", LIBMTP_PROPERTY_PhoneticGivenName, PTP_OPC_PhoneticGivenName);
+  register_property("Phonetic Family Name", LIBMTP_PROPERTY_PhoneticFamilyName, PTP_OPC_PhoneticFamilyName);
+  register_property("Email: Primary", LIBMTP_PROPERTY_EmailPrimary, PTP_OPC_EmailPrimary);
+  register_property("Email: Personal 1", LIBMTP_PROPERTY_EmailPersonal1, PTP_OPC_EmailPersonal1);
+  register_property("Email: Personal 2", LIBMTP_PROPERTY_EmailPersonal2, PTP_OPC_EmailPersonal2);
+  register_property("Email: Business 1", LIBMTP_PROPERTY_EmailBusiness1, PTP_OPC_EmailBusiness1);
+  register_property("Email: Business 2", LIBMTP_PROPERTY_EmailBusiness2, PTP_OPC_EmailBusiness2);
+  register_property("Email: Others", LIBMTP_PROPERTY_EmailOthers, PTP_OPC_EmailOthers);
+  register_property("Phone Number: Primary", LIBMTP_PROPERTY_PhoneNumberPrimary, PTP_OPC_PhoneNumberPrimary);
+  register_property("Phone Number: Personal", LIBMTP_PROPERTY_PhoneNumberPersonal, PTP_OPC_PhoneNumberPersonal);
+  register_property("Phone Number: Personal 2", LIBMTP_PROPERTY_PhoneNumberPersonal2, PTP_OPC_PhoneNumberPersonal2);
+  register_property("Phone Number: Business", LIBMTP_PROPERTY_PhoneNumberBusiness, PTP_OPC_PhoneNumberBusiness);
+  register_property("Phone Number: Business 2", LIBMTP_PROPERTY_PhoneNumberBusiness2, PTP_OPC_PhoneNumberBusiness2);
+  register_property("Phone Number: Mobile", LIBMTP_PROPERTY_PhoneNumberMobile, PTP_OPC_PhoneNumberMobile);
+  register_property("Phone Number: Mobile 2", LIBMTP_PROPERTY_PhoneNumberMobile2, PTP_OPC_PhoneNumberMobile2);
+  register_property("Fax Number: Primary", LIBMTP_PROPERTY_FaxNumberPrimary, PTP_OPC_FaxNumberPrimary);
+  register_property("Fax Number: Personal", LIBMTP_PROPERTY_FaxNumberPersonal, PTP_OPC_FaxNumberPersonal);
+  register_property("Fax Number: Business", LIBMTP_PROPERTY_FaxNumberBusiness, PTP_OPC_FaxNumberBusiness);
+  register_property("Pager Number", LIBMTP_PROPERTY_PagerNumber, PTP_OPC_PagerNumber);
+  register_property("Phone Number: Others", LIBMTP_PROPERTY_PhoneNumberOthers, PTP_OPC_PhoneNumberOthers);
+  register_property("Primary Web Address", LIBMTP_PROPERTY_PrimaryWebAddress, PTP_OPC_PrimaryWebAddress);
+  register_property("Personal Web Address", LIBMTP_PROPERTY_PersonalWebAddress, PTP_OPC_PersonalWebAddress);
+  register_property("Business Web Address", LIBMTP_PROPERTY_BusinessWebAddress, PTP_OPC_BusinessWebAddress);
+  register_property("Instant Messenger Address 1", LIBMTP_PROPERTY_InstantMessengerAddress, PTP_OPC_InstantMessengerAddress);
+  register_property("Instant Messenger Address 2", LIBMTP_PROPERTY_InstantMessengerAddress2, PTP_OPC_InstantMessengerAddress2);
+  register_property("Instant Messenger Address 3", LIBMTP_PROPERTY_InstantMessengerAddress3, PTP_OPC_InstantMessengerAddress3);
+  register_property("Postal Address: Personal: Full", LIBMTP_PROPERTY_PostalAddressPersonalFull, PTP_OPC_PostalAddressPersonalFull);
+  register_property("Postal Address: Personal: Line 1", LIBMTP_PROPERTY_PostalAddressPersonalFullLine1, PTP_OPC_PostalAddressPersonalFullLine1);
+  register_property("Postal Address: Personal: Line 2", LIBMTP_PROPERTY_PostalAddressPersonalFullLine2, PTP_OPC_PostalAddressPersonalFullLine2);
+  register_property("Postal Address: Personal: City", LIBMTP_PROPERTY_PostalAddressPersonalFullCity, PTP_OPC_PostalAddressPersonalFullCity);
+  register_property("Postal Address: Personal: Region", LIBMTP_PROPERTY_PostalAddressPersonalFullRegion, PTP_OPC_PostalAddressPersonalFullRegion);
+  register_property("Postal Address: Personal: Postal Code", LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode, PTP_OPC_PostalAddressPersonalFullPostalCode);
+  register_property("Postal Address: Personal: Country", LIBMTP_PROPERTY_PostalAddressPersonalFullCountry, PTP_OPC_PostalAddressPersonalFullCountry);
+  register_property("Postal Address: Business: Full", LIBMTP_PROPERTY_PostalAddressBusinessFull, PTP_OPC_PostalAddressBusinessFull);
+  register_property("Postal Address: Business: Line 1", LIBMTP_PROPERTY_PostalAddressBusinessLine1, PTP_OPC_PostalAddressBusinessLine1);
+  register_property("Postal Address: Business: Line 2", LIBMTP_PROPERTY_PostalAddressBusinessLine2, PTP_OPC_PostalAddressBusinessLine2);
+  register_property("Postal Address: Business: City", LIBMTP_PROPERTY_PostalAddressBusinessCity, PTP_OPC_PostalAddressBusinessCity);
+  register_property("Postal Address: Business: Region", LIBMTP_PROPERTY_PostalAddressBusinessRegion, PTP_OPC_PostalAddressBusinessRegion);
+  register_property("Postal Address: Business: Postal Code", LIBMTP_PROPERTY_PostalAddressBusinessPostalCode, PTP_OPC_PostalAddressBusinessPostalCode);
+  register_property("Postal Address: Business: Country", LIBMTP_PROPERTY_PostalAddressBusinessCountry, PTP_OPC_PostalAddressBusinessCountry);
+  register_property("Postal Address: Other: Full", LIBMTP_PROPERTY_PostalAddressOtherFull, PTP_OPC_PostalAddressOtherFull);
+  register_property("Postal Address: Other: Line 1", LIBMTP_PROPERTY_PostalAddressOtherLine1, PTP_OPC_PostalAddressOtherLine1);
+  register_property("Postal Address: Other: Line 2", LIBMTP_PROPERTY_PostalAddressOtherLine2, PTP_OPC_PostalAddressOtherLine2);
+  register_property("Postal Address: Other: City", LIBMTP_PROPERTY_PostalAddressOtherCity, PTP_OPC_PostalAddressOtherCity);
+  register_property("Postal Address: Other: Region", LIBMTP_PROPERTY_PostalAddressOtherRegion, PTP_OPC_PostalAddressOtherRegion);
+  register_property("Postal Address: Other: Postal Code", LIBMTP_PROPERTY_PostalAddressOtherPostalCode, PTP_OPC_PostalAddressOtherPostalCode);
+  register_property("Postal Address: Other: Counrtry", LIBMTP_PROPERTY_PostalAddressOtherCountry, PTP_OPC_PostalAddressOtherCountry);
+  register_property("Organization Name", LIBMTP_PROPERTY_OrganizationName, PTP_OPC_OrganizationName);
+  register_property("Phonetic Organization Name", LIBMTP_PROPERTY_PhoneticOrganizationName, PTP_OPC_PhoneticOrganizationName);
+  register_property("Role", LIBMTP_PROPERTY_Role, PTP_OPC_Role);
+  register_property("Birthdate", LIBMTP_PROPERTY_Birthdate, PTP_OPC_Birthdate);
+  register_property("Message To", LIBMTP_PROPERTY_MessageTo, PTP_OPC_MessageTo);
+  register_property("Message CC", LIBMTP_PROPERTY_MessageCC, PTP_OPC_MessageCC);
+  register_property("Message BCC", LIBMTP_PROPERTY_MessageBCC, PTP_OPC_MessageBCC);
+  register_property("Message Read", LIBMTP_PROPERTY_MessageRead, PTP_OPC_MessageRead);
+  register_property("Message Received Time", LIBMTP_PROPERTY_MessageReceivedTime, PTP_OPC_MessageReceivedTime);
+  register_property("Message Sender", LIBMTP_PROPERTY_MessageSender, PTP_OPC_MessageSender);
+  register_property("Activity Begin Time", LIBMTP_PROPERTY_ActivityBeginTime, PTP_OPC_ActivityBeginTime);
+  register_property("Activity End Time", LIBMTP_PROPERTY_ActivityEndTime, PTP_OPC_ActivityEndTime);
+  register_property("Activity Location", LIBMTP_PROPERTY_ActivityLocation, PTP_OPC_ActivityLocation);
+  register_property("Activity Required Attendees", LIBMTP_PROPERTY_ActivityRequiredAttendees, PTP_OPC_ActivityRequiredAttendees);
+  register_property("Optional Attendees", LIBMTP_PROPERTY_ActivityOptionalAttendees, PTP_OPC_ActivityOptionalAttendees);
+  register_property("Activity Resources", LIBMTP_PROPERTY_ActivityResources, PTP_OPC_ActivityResources);
+  register_property("Activity Accepted", LIBMTP_PROPERTY_ActivityAccepted, PTP_OPC_ActivityAccepted);
+  register_property("Owner", LIBMTP_PROPERTY_Owner, PTP_OPC_Owner);
+  register_property("Editor", LIBMTP_PROPERTY_Editor, PTP_OPC_Editor);
+  register_property("Webmaster", LIBMTP_PROPERTY_Webmaster, PTP_OPC_Webmaster);
+  register_property("URL Source", LIBMTP_PROPERTY_URLSource, PTP_OPC_URLSource);
+  register_property("URL Destination", LIBMTP_PROPERTY_URLDestination, PTP_OPC_URLDestination);
+  register_property("Time Bookmark", LIBMTP_PROPERTY_TimeBookmark, PTP_OPC_TimeBookmark);
+  register_property("Object Bookmark", LIBMTP_PROPERTY_ObjectBookmark, PTP_OPC_ObjectBookmark);
+  register_property("Byte Bookmark", LIBMTP_PROPERTY_ByteBookmark, PTP_OPC_ByteBookmark);
+  register_property("Last Build Date", LIBMTP_PROPERTY_LastBuildDate, PTP_OPC_LastBuildDate);
+  register_property("Time To Live", LIBMTP_PROPERTY_TimetoLive, PTP_OPC_TimetoLive);
+  register_property("Media GUID", LIBMTP_PROPERTY_MediaGUID, PTP_OPC_MediaGUID);
+  register_property("Total Bit Rate", LIBMTP_PROPERTY_TotalBitRate, PTP_OPC_TotalBitRate);
+  register_property("Bit Rate Type", LIBMTP_PROPERTY_BitRateType, PTP_OPC_BitRateType);
+  register_property("Sample Rate", LIBMTP_PROPERTY_SampleRate, PTP_OPC_SampleRate);
+  register_property("Number Of Channels", LIBMTP_PROPERTY_NumberOfChannels, PTP_OPC_NumberOfChannels);
+  register_property("Audio Bit Depth", LIBMTP_PROPERTY_AudioBitDepth, PTP_OPC_AudioBitDepth);
+  register_property("Scan Depth", LIBMTP_PROPERTY_ScanDepth, PTP_OPC_ScanDepth);
+  register_property("Audio WAVE Codec", LIBMTP_PROPERTY_AudioWAVECodec, PTP_OPC_AudioWAVECodec);
+  register_property("Audio Bit Rate", LIBMTP_PROPERTY_AudioBitRate, PTP_OPC_AudioBitRate);
+  register_property("Video Four CC Codec", LIBMTP_PROPERTY_VideoFourCCCodec, PTP_OPC_VideoFourCCCodec);
+  register_property("Video Bit Rate", LIBMTP_PROPERTY_VideoBitRate, PTP_OPC_VideoBitRate);
+  register_property("Frames Per Thousand Seconds", LIBMTP_PROPERTY_FramesPerThousandSeconds, PTP_OPC_FramesPerThousandSeconds);
+  register_property("Key Frame Distance", LIBMTP_PROPERTY_KeyFrameDistance, PTP_OPC_KeyFrameDistance);
+  register_property("Buffer Size", LIBMTP_PROPERTY_BufferSize, PTP_OPC_BufferSize);
+  register_property("Encoding Quality", LIBMTP_PROPERTY_EncodingQuality, PTP_OPC_EncodingQuality);
+  register_property("Encoding Profile", LIBMTP_PROPERTY_EncodingProfile, PTP_OPC_EncodingProfile);
+  register_property("Buy flag", LIBMTP_PROPERTY_BuyFlag, PTP_OPC_BuyFlag);
+  register_property("Unknown property", LIBMTP_PROPERTY_UNKNOWN, 0);
+}
+
+/**
+ * Returns the PTP property that maps to a certain libmtp internal property type.
+ * @param inproperty the MTP library interface property
+ * @return the PTP (libgphoto2) property type
+ */
+static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty)
+{
+  propertymap_t *current;
+
+  current = propertymap;
+
+  while (current != NULL) {
+    if(current->id == inproperty) {
+      return current->ptp_id;
+    }
+    current = current->next;
+  }
+  return 0;
+}
+
+
+/**
+ * Returns the MTP internal interface property that maps to a certain ptp
+ * interface property.
+ * @param inproperty the PTP (libgphoto2) interface property
+ * @return the MTP library interface property
+ */
+static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t inproperty)
+{
+  propertymap_t *current;
+
+  current = propertymap;
+
+  while (current != NULL) {
+    if(current->ptp_id == inproperty) {
+      return current->id;
+    }
+    current = current->next;
+  }
+  // printf("map_ptp_type_to_libmtp_type: unknown filetype.\n");
+  return LIBMTP_PROPERTY_UNKNOWN;
+}
+
+
+/**
+ * Initialize the library. You are only supposed to call this
+ * one, before using the library for the first time in a program.
+ * Never re-initialize libmtp!
+ *
+ * The only thing this does at the moment is to initialise the
+ * filetype mapping table.
+ */
+void LIBMTP_Init(void)
+{
+  init_filemap();
+  init_propertymap();
+  return;
+}
+
+
+/**
+ * This helper function returns a textual description for a libmtp
+ * file type to be used in dialog boxes etc.
+ * @param intype the libmtp internal filetype to get a description for.
+ * @return a string representing the filetype, this must <b>NOT</b>
+ *         be free():ed by the caller!
+ */
+char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
+{
+  filemap_t *current;
+
+  current = filemap;
+
+  while (current != NULL) {
+    if(current->id == intype) {
+      return current->description;
+    }
+    current = current->next;
+  }
+
+  return "Unknown filetype";
+}
+
+/**
+ * This helper function returns a textual description for a libmtp
+ * property to be used in dialog boxes etc.
+ * @param inproperty the libmtp internal property to get a description for.
+ * @return a string representing the filetype, this must <b>NOT</b>
+ *         be free():ed by the caller!
+ */
+char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty)
+{
+  propertymap_t *current;
+
+  current = propertymap;
+
+  while (current != NULL) {
+    if(current->id == inproperty) {
+      return current->description;
+    }
+    current = current->next;
+  }
+
+  return "Unknown property";
+}
+
+/**
+ * This function will do its best to fit a 16bit
+ * value into a PTP object property if the property
+ * is limited in range or step sizes.
+ */
+static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd)
+{
+  switch (opd->FormFlag) {
+  case PTP_DPFF_Range:
+    if (val < opd->FORM.Range.MinimumValue.u16) {
+      return opd->FORM.Range.MinimumValue.u16;
+    }
+    if (val > opd->FORM.Range.MaximumValue.u16) {
+      return opd->FORM.Range.MaximumValue.u16;
+    }
+    // Round down to last step.
+    if (val % opd->FORM.Range.StepSize.u16 != 0) {
+      return val - (val % opd->FORM.Range.StepSize.u16);
+    }
+    return val;
+    break;
+  case PTP_DPFF_Enumeration:
+    {
+      int i;
+      uint16_t bestfit = opd->FORM.Enum.SupportedValue[0].u16;
+      
+      for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
+	if (val == opd->FORM.Enum.SupportedValue[i].u16) {
+	  return val;
+	}
+	// Rough guess of best fit
+	if (opd->FORM.Enum.SupportedValue[i].u16 < val) {
+	  bestfit = opd->FORM.Enum.SupportedValue[i].u16;
+	}
+      }
+      // Just some default that'll work.
+      return bestfit;
+    }
+  default:
+    // Will accept any value
+    break;
+  }
+  return val;
+}
+
+/**
+ * This function will do its best to fit a 32bit
+ * value into a PTP object property if the property
+ * is limited in range or step sizes.
+ */
+static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd)
+{
+  switch (opd->FormFlag) {
+  case PTP_DPFF_Range:
+    if (val < opd->FORM.Range.MinimumValue.u32) {
+      return opd->FORM.Range.MinimumValue.u32;
+    }
+    if (val > opd->FORM.Range.MaximumValue.u32) {
+      return opd->FORM.Range.MaximumValue.u32;
+    }
+    // Round down to last step.
+    if (val % opd->FORM.Range.StepSize.u32 != 0) {
+      return val - (val % opd->FORM.Range.StepSize.u32);
+    }
+    return val;
+    break;
+  case PTP_DPFF_Enumeration:
+    {
+      int i;
+      uint32_t bestfit = opd->FORM.Enum.SupportedValue[0].u32;
+
+      for (i=0; i<opd->FORM.Enum.NumberOfValues; i++) {
+	if (val == opd->FORM.Enum.SupportedValue[i].u32) {
+	  return val;
+	}
+	// Rough guess of best fit
+	if (opd->FORM.Enum.SupportedValue[i].u32 < val) {
+	  bestfit = opd->FORM.Enum.SupportedValue[i].u32;
+	}
+      }
+      // Just some default that'll work.
+      return bestfit;
+    }
+  default:
+    // Will accept any value
+    break;
+  }
+  return val;
+}
+
+/**
+ * This function returns a newly created ISO 8601 timestamp with the
+ * current time in as high precision as possible. It even adds
+ * the time zone if it can.
+ */
+static char *get_iso8601_stamp(void)
+{
+  time_t curtime;
+  struct tm *loctime;
+  char tmp[64];
+
+  curtime = time(NULL);
+  loctime = localtime(&curtime);
+  strftime (tmp, sizeof(tmp), "%Y%m%dT%H%M%S.0%z", loctime);
+  return strdup(tmp);
+}
+
+/**
+ * Gets the allowed values (range or enum) for a property
+ * @param device a pointer to an MTP device
+ * @param property the property to query
+ * @param filetype the filetype of the object you want to set values for
+ * @param allowed_vals pointer to a LIBMTP_allowed_values_t struct to
+ *        receive the allowed values.  Call LIBMTP_destroy_allowed_values_t
+ *        on this on successful completion.
+ * @return 0 on success, any other value means failure
+ */
+int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
+            LIBMTP_filetype_t const filetype, LIBMTP_allowed_values_t *allowed_vals)
+{
+  PTPObjectPropDesc opd;
+  uint16_t ret = 0;
+  
+  ret = ptp_mtp_getobjectpropdesc(device->params, map_libmtp_property_to_ptp_property(property), map_libmtp_type_to_ptp_type(filetype), &opd);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Allowed_Property_Values(): could not get property description.");
+    return -1;
+  }
+
+  if (opd.FormFlag == PTP_OPFF_Enumeration) {
+    int i = 0;
+    
+    allowed_vals->is_range = 0;
+    allowed_vals->num_entries = opd.FORM.Enum.NumberOfValues;
+
+    switch (opd.DataType)
+    {
+      case PTP_DTC_INT8:
+        allowed_vals->i8vals = malloc(sizeof(int8_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
+        break;
+      case PTP_DTC_UINT8:
+        allowed_vals->u8vals = malloc(sizeof(uint8_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
+        break;
+      case PTP_DTC_INT16:
+        allowed_vals->i16vals = malloc(sizeof(int16_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
+        break;
+      case PTP_DTC_UINT16:
+        allowed_vals->u16vals = malloc(sizeof(uint16_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
+        break;
+      case PTP_DTC_INT32:
+        allowed_vals->i32vals = malloc(sizeof(int32_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
+        break;
+      case PTP_DTC_UINT32:
+        allowed_vals->u32vals = malloc(sizeof(uint32_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
+        break;
+      case PTP_DTC_INT64:
+        allowed_vals->i64vals = malloc(sizeof(int64_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
+        break;
+      case PTP_DTC_UINT64:
+        allowed_vals->u64vals = malloc(sizeof(uint64_t) * opd.FORM.Enum.NumberOfValues);
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
+        break;
+    }
+    
+    for (i = 0; i < opd.FORM.Enum.NumberOfValues; i++) {
+      switch (opd.DataType)
+      {
+        case PTP_DTC_INT8:
+          allowed_vals->i8vals[i] = opd.FORM.Enum.SupportedValue[i].i8;
+          break;
+        case PTP_DTC_UINT8:
+          allowed_vals->u8vals[i] = opd.FORM.Enum.SupportedValue[i].u8;
+          break;
+        case PTP_DTC_INT16:
+          allowed_vals->i16vals[i] = opd.FORM.Enum.SupportedValue[i].i16;
+          break;
+        case PTP_DTC_UINT16:
+          allowed_vals->u16vals[i] = opd.FORM.Enum.SupportedValue[i].u16;
+          break;
+        case PTP_DTC_INT32:
+          allowed_vals->i32vals[i] = opd.FORM.Enum.SupportedValue[i].i32;
+          break;
+        case PTP_DTC_UINT32:
+          allowed_vals->u32vals[i] = opd.FORM.Enum.SupportedValue[i].u32;
+          break;
+        case PTP_DTC_INT64:
+          allowed_vals->i64vals[i] = opd.FORM.Enum.SupportedValue[i].i64;
+          break;
+        case PTP_DTC_UINT64:
+          allowed_vals->u64vals[i] = opd.FORM.Enum.SupportedValue[i].u64;
+          break;
+      }
+    }
+    ptp_free_objectpropdesc(&opd);
+    return 0;
+  } else if (opd.FormFlag == PTP_OPFF_Range) {
+    allowed_vals->is_range = 1;
+    
+    switch (opd.DataType)
+    {
+      case PTP_DTC_INT8:
+        allowed_vals->i8min = opd.FORM.Range.MinimumValue.i8;
+        allowed_vals->i8max = opd.FORM.Range.MaximumValue.i8;
+        allowed_vals->i8step = opd.FORM.Range.StepSize.i8;
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT8;
+        break;
+      case PTP_DTC_UINT8:
+        allowed_vals->u8min = opd.FORM.Range.MinimumValue.u8;
+        allowed_vals->u8max = opd.FORM.Range.MaximumValue.u8;
+        allowed_vals->u8step = opd.FORM.Range.StepSize.u8;
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT8;
+        break;
+      case PTP_DTC_INT16:
+        allowed_vals->i16min = opd.FORM.Range.MinimumValue.i16;
+        allowed_vals->i16max = opd.FORM.Range.MaximumValue.i16;
+        allowed_vals->i16step = opd.FORM.Range.StepSize.i16;
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT16;
+        break;
+      case PTP_DTC_UINT16:
+        allowed_vals->u16min = opd.FORM.Range.MinimumValue.u16;
+        allowed_vals->u16max = opd.FORM.Range.MaximumValue.u16;
+        allowed_vals->u16step = opd.FORM.Range.StepSize.u16;
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT16;
+        break;
+      case PTP_DTC_INT32:
+        allowed_vals->i32min = opd.FORM.Range.MinimumValue.i32;
+        allowed_vals->i32max = opd.FORM.Range.MaximumValue.i32;
+        allowed_vals->i32step = opd.FORM.Range.StepSize.i32;
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT32;
+        break;
+      case PTP_DTC_UINT32:
+        allowed_vals->u32min = opd.FORM.Range.MinimumValue.u32;
+        allowed_vals->u32max = opd.FORM.Range.MaximumValue.u32;
+        allowed_vals->u32step = opd.FORM.Range.StepSize.u32;
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT32;
+        break;
+      case PTP_DTC_INT64:
+        allowed_vals->i64min = opd.FORM.Range.MinimumValue.i64;
+        allowed_vals->i64max = opd.FORM.Range.MaximumValue.i64;
+        allowed_vals->i64step = opd.FORM.Range.StepSize.i64;
+        allowed_vals->datatype = LIBMTP_DATATYPE_INT64;
+        break;
+      case PTP_DTC_UINT64:
+        allowed_vals->u64min = opd.FORM.Range.MinimumValue.u64;
+        allowed_vals->u64max = opd.FORM.Range.MaximumValue.u64;
+        allowed_vals->u64step = opd.FORM.Range.StepSize.u64;
+        allowed_vals->datatype = LIBMTP_DATATYPE_UINT64;
+        break;
+    }
+    return 0; 
+  } else
+    return -1;
+}
+
+/**
+ * Destroys a LIBMTP_allowed_values_t struct
+ * @param allowed_vals the struct to destroy
+ */
+void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t *allowed_vals)
+{
+  if (!allowed_vals->is_range)
+  {
+    switch (allowed_vals->datatype)
+    {
+      case LIBMTP_DATATYPE_INT8:
+        if (allowed_vals->i8vals)
+          free(allowed_vals->i8vals);
+        break;
+      case LIBMTP_DATATYPE_UINT8:
+        if (allowed_vals->u8vals)
+          free(allowed_vals->u8vals);
+        break;
+      case LIBMTP_DATATYPE_INT16:
+        if (allowed_vals->i16vals)
+          free(allowed_vals->i16vals);
+        break;
+      case LIBMTP_DATATYPE_UINT16:
+        if (allowed_vals->u16vals)
+          free(allowed_vals->u16vals);
+        break;
+      case LIBMTP_DATATYPE_INT32:
+        if (allowed_vals->i32vals)
+          free(allowed_vals->i32vals);
+        break;
+      case LIBMTP_DATATYPE_UINT32:
+        if (allowed_vals->u32vals)
+          free(allowed_vals->u32vals);
+        break;
+      case LIBMTP_DATATYPE_INT64:
+        if (allowed_vals->i64vals)
+          free(allowed_vals->i64vals);
+        break;
+      case LIBMTP_DATATYPE_UINT64:
+        if (allowed_vals->u64vals)
+          free(allowed_vals->u64vals);
+        break;
+    }
+  }
+}
+
+/**
+ * Determine if a property is supported for a given file type
+ * @param device a pointer to an MTP device
+ * @param property the property to query
+ * @param filetype the filetype of the object you want to set values for
+ * @return 0 if not supported, positive if supported, negative on error
+ */
+int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t *device, LIBMTP_property_t const property,
+            LIBMTP_filetype_t const filetype)
+{
+  uint16_t *props = NULL;
+  uint32_t propcnt = 0;
+  uint16_t ret = 0;
+  int i = 0;
+  int supported = 0;
+  uint16_t ptp_prop = map_libmtp_property_to_ptp_property(property);
+  
+  ret = ptp_mtp_getobjectpropssupported(device->params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Is_Property_Supported(): could not get properties supported.");
+    return -1;
+  }
+
+	for (i = 0; i < propcnt; i++) {
+    if (props[i] == ptp_prop) {
+      supported = 1;
+      break;
+    }
+  }
+  
+  free(props);
+  
+  return supported;
+}
+
+/**
+ * Retrieves a string from an object
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @return valid string or NULL on failure. The returned string
+ *         must bee <code>free()</code>:ed by the caller after
+ *         use.
+ */
+char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    LIBMTP_property_t const attribute_id)
+{
+  return get_string_from_object(device, object_id, attribute_id);
+}
+
+/**
+* Retrieves an unsigned 64-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return the value
+ */
+uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+                                    LIBMTP_property_t const attribute_id, uint64_t const value_default)
+{
+  return get_u64_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
+}
+
+/**
+ * Retrieves an unsigned 32-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return the value
+ */
+uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+				    LIBMTP_property_t const attribute_id, uint32_t const value_default)
+{
+  return get_u32_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
+}
+
+/**
+ * Retrieves an unsigned 16-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return a value
+ */
+uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    LIBMTP_property_t const attribute_id, uint16_t const value_default)
+{
+  return get_u16_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
+}
+
+/**
+ * Retrieves an unsigned 8-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return a value
+ */
+uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				  LIBMTP_property_t const attribute_id, uint8_t const value_default)
+{
+  return get_u8_from_object(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value_default);
+}
+
+/**
+ * Sets an object attribute from a string
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param string string value to set
+ * @return 0 on success, any other value means failure
+ */
+int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			     LIBMTP_property_t const attribute_id, char const * const string)
+{
+  return set_object_string(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), string);
+}
+
+
+/**
+ * Sets an object attribute from an unsigned 32-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value 32-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  LIBMTP_property_t const attribute_id, uint32_t const value)
+{
+  return set_object_u32(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
+}
+
+/**
+ * Sets an object attribute from an unsigned 16-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value 16-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  LIBMTP_property_t const attribute_id, uint16_t const value)
+{
+  return set_object_u16(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
+}
+
+/**
+ * Sets an object attribute from an unsigned 8-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id MTP attribute ID
+ * @param value 8-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			 LIBMTP_property_t const attribute_id, uint8_t const value)
+{
+  return set_object_u8(device, object_id, map_libmtp_property_to_ptp_property(attribute_id), value);
+}
+
+/**
+ * Retrieves a string from an object
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @return valid string or NULL on failure. The returned string
+ *         must bee <code>free()</code>:ed by the caller after
+ *         use.
+ */
+static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    uint16_t const attribute_id)
+{
+  PTPPropertyValue propval;
+  char *retstring = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  MTPProperties *prop;
+
+  if ( device == NULL || object_id == 0) {
+    return NULL;
+  }
+  
+  prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
+  if (prop) {
+    if (prop->propval.str != NULL)
+      return strdup(prop->propval.str);
+    else
+      return NULL;
+  }
+
+  ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
+  if (ret == PTP_RC_OK) {
+    if (propval.str != NULL) {
+      retstring = (char *) strdup(propval.str);
+      free(propval.str);
+    }
+  } else {
+    add_ptp_error_to_errorstack(device, ret, "get_string_from_object(): could not get object string.");
+  }
+
+  return retstring;
+}
+
+/**
+* Retrieves an unsigned 64-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return the value
+ */
+static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+                                    uint16_t const attribute_id, uint64_t const value_default)
+{
+  PTPPropertyValue propval;
+  uint64_t retval = value_default;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  MTPProperties *prop;
+  
+  if ( device == NULL ) {
+    return value_default;
+  }
+  
+  prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
+  if (prop)
+    return prop->propval.u64;
+
+  ret = ptp_mtp_getobjectpropvalue(params, object_id,
+                                   attribute_id,
+                                   &propval,
+                                   PTP_DTC_UINT64);
+  if (ret == PTP_RC_OK) {
+    retval = propval.u64;
+  } else {
+    add_ptp_error_to_errorstack(device, ret, "get_u64_from_object(): could not get unsigned 64bit integer from object.");
+  }
+  
+  return retval;
+}
+
+/**
+ * Retrieves an unsigned 32-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return the value
+ */
+static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id,
+				    uint16_t const attribute_id, uint32_t const value_default)
+{
+  PTPPropertyValue propval;
+  uint32_t retval = value_default;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  MTPProperties *prop;
+
+  if ( device == NULL ) {
+    return value_default;
+  }
+
+  prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
+  if (prop)
+    return prop->propval.u32;
+
+  ret = ptp_mtp_getobjectpropvalue(params, object_id,
+                                   attribute_id,
+                                   &propval,
+                                   PTP_DTC_UINT32);
+  if (ret == PTP_RC_OK) {
+    retval = propval.u32;
+  } else {
+    add_ptp_error_to_errorstack(device, ret, "get_u32_from_object(): could not get unsigned 32bit integer from object.");
+  }
+  return retval;
+}
+
+/**
+ * Retrieves an unsigned 16-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return a value
+ */
+static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				    uint16_t const attribute_id, uint16_t const value_default)
+{
+  PTPPropertyValue propval;
+  uint16_t retval = value_default;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  MTPProperties *prop;
+
+  if ( device == NULL ) {
+    return value_default;
+  }
+
+  // This O(n) search should not be used so often, since code
+  // using the cached properties don't usually call this function.
+  prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
+  if (prop)
+    return prop->propval.u16;
+
+  ret = ptp_mtp_getobjectpropvalue(params, object_id,
+                                   attribute_id,
+                                   &propval,
+                                   PTP_DTC_UINT16);
+  if (ret == PTP_RC_OK) {
+    retval = propval.u16;
+  } else {
+    add_ptp_error_to_errorstack(device, ret, "get_u16_from_object(): could not get unsigned 16bit integer from object.");
+  }
+
+  return retval;
+}
+
+/**
+ * Retrieves an unsigned 8-bit integer from an object attribute
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value_default Default value to return on failure
+ * @return a value
+ */
+static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+				  uint16_t const attribute_id, uint8_t const value_default)
+{
+  PTPPropertyValue propval;
+  uint8_t retval = value_default;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  MTPProperties *prop;
+
+  if ( device == NULL ) {
+    return value_default;
+  }
+
+  // This O(n) search should not be used so often, since code
+  // using the cached properties don't usually call this function.
+  prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
+  if (prop)
+    return prop->propval.u8;
+
+  ret = ptp_mtp_getobjectpropvalue(params, object_id,
+                                   attribute_id,
+                                   &propval,
+                                   PTP_DTC_UINT8);
+  if (ret == PTP_RC_OK) {
+    retval = propval.u8;
+  } else {
+    add_ptp_error_to_errorstack(device, ret, "get_u8_from_object(): could not get unsigned 8bit integer from object.");
+  }
+
+  return retval;
+}
+
+/**
+ * Sets an object attribute from a string
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param string string value to set
+ * @return 0 on success, any other value means failure
+ */
+static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			     uint16_t const attribute_id, char const * const string)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (device == NULL || string == NULL) {
+    return -1;
+  }
+  
+  if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
+				"PTP_OC_MTP_SetObjectPropValue not supported.");
+    return -1;
+  }
+  propval.str = (char *) string;
+  ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_STR);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "set_object_string(): could not set object string.");
+    return -1;
+  }
+
+  return 0;
+}
+
+
+/**
+ * Sets an object attribute from an unsigned 32-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value 32-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  uint16_t const attribute_id, uint32_t const value)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (device == NULL) {
+    return -1;
+  }
+
+  if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
+				"PTP_OC_MTP_SetObjectPropValue not supported.");
+    return -1;
+  }
+  
+  propval.u32 = value;
+  ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "set_object_u32(): could not set unsigned 32bit integer property.");
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * Sets an object attribute from an unsigned 16-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value 16-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			  uint16_t const attribute_id, uint16_t const value)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (device == NULL) {
+    return 1;
+  }
+
+  if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
+				"PTP_OC_MTP_SetObjectPropValue not supported.");
+    return -1;
+  }
+  propval.u16 = value;
+  ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT16);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "set_object_u16(): could not set unsigned 16bit integer property.");
+    return 1;
+  }
+
+  return 0;
+}
+
+/**
+ * Sets an object attribute from an unsigned 8-bit integer
+ *
+ * @param device a pointer to an MTP device.
+ * @param object_id Object reference
+ * @param attribute_id PTP attribute ID
+ * @param value 8-bit unsigned integer to set
+ * @return 0 on success, any other value means failure
+ */
+static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
+			 uint16_t const attribute_id, uint8_t const value)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (device == NULL) {
+    return 1;
+  }
+
+  if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
+			    "PTP_OC_MTP_SetObjectPropValue not supported.");
+    return -1;
+  }
+  propval.u8 = value;
+  ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT8);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "set_object_u8(): could not set unsigned 8bit integer property.");
+    return 1;
+  }
+
+  return 0;
+}
+
+/**
+ * Get the first (as in "first in the list of") connected MTP device.
+ * @return a device pointer.
+ * @see LIBMTP_Get_Connected_Devices()
+ */
+LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
+{
+  LIBMTP_mtpdevice_t *first_device = NULL;
+  LIBMTP_raw_device_t *devices;
+  int numdevs;
+  LIBMTP_error_number_t ret;
+  
+  ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
+  if (ret != LIBMTP_ERROR_NONE) {
+    return NULL;
+  }
+
+  if (devices == NULL || numdevs == 0) {
+    return NULL;
+  }
+
+  first_device = LIBMTP_Open_Raw_Device(&devices[0]);
+  free(devices);
+  return first_device;
+}
+
+/**
+ * Overriding debug function.
+ * This way we can disable debug prints.
+ */
+static void
+#ifdef __GNUC__
+__attribute__((__format__(printf,2,0)))
+#endif
+LIBMTP_ptp_debug(void *data, const char *format, va_list args)
+{
+#ifdef ENABLE_PTP_DEBUG
+  vfprintf (stderr, format, args);
+  fflush (stderr);
+#endif
+}
+
+/**
+ * Overriding error function.
+ * This way we can capture all error etc to our errorstack.
+ */
+static void
+#ifdef __GNUC__
+__attribute__((__format__(printf,2,0)))
+#endif
+LIBMTP_ptp_error(void *data, const char *format, va_list args)
+{
+  // if (data == NULL) {
+    vfprintf (stderr, format, args);
+    fflush (stderr);
+  /*
+    FIXME: find out how we shall get the device here.
+  } else {
+    PTP_USB *ptp_usb = data;
+    LIBMTP_mtpdevice_t *device = ...;
+    char buf[2048];
+
+    vsnprintf (buf, sizeof (buf), format, args);
+    add_error_to_errorstack(device,
+			    LIBMTP_ERROR_PTP_LAYER,
+			    buf);
+  }
+  */
+}
+
+/**
+ * This function opens a device from a raw device. It is the
+ * preferred way to access devices in the new interface where
+ * several devices can come and go as the library is working
+ * on a certain device.
+ * @param rawdevice the raw device to open a "real" device for.
+ * @return an open device.
+ */
+LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
+{
+  LIBMTP_mtpdevice_t *mtp_device;
+  uint8_t bs = 0;
+  PTPParams *current_params;
+  PTP_USB *ptp_usb;
+  LIBMTP_error_number_t err;
+  int i;
+
+  /* Allocate dynamic space for our device */
+  mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
+  memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
+  /* Check if there was a memory allocation error */
+  if(mtp_device == NULL) {
+    /* There has been an memory allocation error. We are going to ignore this
+       device and attempt to continue */
+    
+    /* TODO: This error statement could probably be a bit more robust */
+    fprintf(stderr, "LIBMTP PANIC: connect_usb_devices encountered a memory "
+	    "allocation error with device %d on bus %d, trying to continue",
+	    rawdevice->devnum, rawdevice->bus_location);
+    
+    return NULL;
+  }
+
+  /* Create PTP params */
+  current_params = (PTPParams *) malloc(sizeof(PTPParams));
+  if (current_params == NULL) {
+    free(mtp_device);
+    return NULL;
+  }
+  memset(current_params, 0, sizeof(PTPParams));
+  current_params->device_flags = rawdevice->device_entry.device_flags;
+  current_params->nrofobjects = 0;
+  current_params->objects = NULL;
+  current_params->response_packet_size = 0;
+  current_params->response_packet = NULL;
+  /* This will be a pointer to PTP_USB later */
+  current_params->data = NULL;
+  /* Set upp local debug and error functions */
+  current_params->debug_func = LIBMTP_ptp_debug;
+  current_params->error_func = LIBMTP_ptp_error;
+  /* TODO: Will this always be little endian? */
+  current_params->byteorder = PTP_DL_LE;
+  current_params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8");
+  current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UCS-2LE");
+    
+  if(current_params->cd_locale_to_ucs2 == (iconv_t) -1 ||
+     current_params->cd_ucs2_to_locale == (iconv_t) -1) {
+    fprintf(stderr, "LIBMTP PANIC: Cannot open iconv() converters to/from UCS-2!\n"
+	    "Too old stdlibc, glibc and libiconv?\n");
+    free(current_params);
+    free(mtp_device);
+    return NULL;
+  }
+  mtp_device->params = current_params;
+
+  
+  /* Create usbinfo, this also opens the session */
+  err = configure_usb_device(rawdevice,
+			     current_params,
+			     &mtp_device->usbinfo);
+  if (err != LIBMTP_ERROR_NONE) {
+    free(current_params);
+    free(mtp_device);
+    return NULL;
+  }
+  ptp_usb = (PTP_USB*) mtp_device->usbinfo;
+  /* Set pointer back to params */
+  ptp_usb->params = current_params;
+
+  
+  /* Cache the device information for later use */
+  if (ptp_getdeviceinfo(current_params,
+			&current_params->deviceinfo) != PTP_RC_OK) {
+    fprintf(stderr, "LIBMTP PANIC: Unable to read device information on device "
+	    "%d on bus %d, trying to continue",
+	    rawdevice->devnum, rawdevice->bus_location);
+    
+    /* Prevent memory leaks for this device */
+    free(mtp_device->usbinfo);
+    free(mtp_device->params);
+    current_params = NULL;
+    free(mtp_device);    
+    return NULL;
+  }
+  
+  /* Determine if the object size supported is 32 or 64 bit wide */
+  for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
+    PTPObjectPropDesc opd;
+    
+    if (ptp_mtp_getobjectpropdesc(current_params, 
+				  PTP_OPC_ObjectSize, 
+				  current_params->deviceinfo.ImageFormats[i], 
+				  &opd) != PTP_RC_OK) {
+      printf("LIBMTP PANIC: "
+	     "could not inspect object property descriptions!\n");
+    } else {
+      if (opd.DataType == PTP_DTC_UINT32) {
+	if (bs == 0) {
+	  bs = 32;
+	} else if (bs != 32) {
+	  printf("LIBMTP PANIC: "
+		 "different objects support different object sizes!\n");
+	  bs = 0;
+	  break;
+	}
+      } else if (opd.DataType == PTP_DTC_UINT64) {
+	if (bs == 0) {
+	  bs = 64;
+	} else if (bs != 64) {
+	  printf("LIBMTP PANIC: "
+		 "different objects support different object sizes!\n");
+	  bs = 0;
+	  break;
+	}
+      } else {
+	// Ignore if other size.
+	printf("LIBMTP PANIC: "
+	       "awkward object size data type: %04x\n", opd.DataType);
+	bs = 0;
+	break;
+      }
+    }
+  }
+  if (bs == 0) {
+    // Could not detect object bitsize, assume 32 bits
+    bs = 32;
+  }
+  mtp_device->object_bitsize = bs;
+  
+  /* No Errors yet for this device */
+  mtp_device->errorstack = NULL;
+  
+  /* Default Max Battery Level, we will adjust this if possible */
+  mtp_device->maximum_battery_level = 100;
+  
+  /* Check if device supports reading maximum battery level */
+  if(!FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) && 
+     ptp_property_issupported( current_params, PTP_DPC_BatteryLevel)) {
+    PTPDevicePropDesc dpd;
+    
+    /* Try to read maximum battery level */
+    if(ptp_getdevicepropdesc(current_params,
+			     PTP_DPC_BatteryLevel,
+			     &dpd) != PTP_RC_OK) {
+      add_error_to_errorstack(mtp_device,
+			      LIBMTP_ERROR_CONNECTING,
+			      "Unable to read Maximum Battery Level for this "
+			      "device even though the device supposedly "
+			      "supports this functionality");
+    }
+    
+    /* TODO: is this appropriate? */
+    /* If max battery level is 0 then leave the default, otherwise assign */
+    if (dpd.FORM.Range.MaximumValue.u8 != 0) {
+      mtp_device->maximum_battery_level = dpd.FORM.Range.MaximumValue.u8;
+    }
+    
+    ptp_free_devicepropdesc(&dpd);
+  }
+  
+  /* Set all default folders to 0 (root directory) */
+  mtp_device->default_music_folder = 0;
+  mtp_device->default_playlist_folder = 0;
+  mtp_device->default_picture_folder = 0;
+  mtp_device->default_video_folder = 0;
+  mtp_device->default_organizer_folder = 0;
+  mtp_device->default_zencast_folder = 0;
+  mtp_device->default_album_folder = 0;
+  mtp_device->default_text_folder = 0;
+  
+  /* Set initial storage information */
+  mtp_device->storage = NULL;
+  if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) {
+    add_error_to_errorstack(mtp_device,
+			    LIBMTP_ERROR_GENERAL,
+			    "Get Storage information failed.");
+    mtp_device->storage = NULL;
+  }
+  
+  /*
+   * Then get the handles and try to locate the default folders.
+   * This has the desired side effect of caching all handles from
+   * the device which speeds up later operations.
+   */
+  flush_handles(mtp_device);
+
+  return mtp_device;
+}
+
+/**
+ * Recursive function that adds MTP devices to a linked list
+ * @param devices a list of raw devices to have real devices created for.
+ * @return a device pointer to a newly created mtpdevice (used in linked
+ * list creation).
+ */
+static LIBMTP_mtpdevice_t * create_usb_mtp_devices(LIBMTP_raw_device_t *devices, int numdevs)
+{
+  uint8_t i;
+  LIBMTP_mtpdevice_t *mtp_device_list = NULL;
+  LIBMTP_mtpdevice_t *current_device = NULL;
+
+  for (i=0; i < numdevs; i++) {
+    LIBMTP_mtpdevice_t *mtp_device;
+    mtp_device = LIBMTP_Open_Raw_Device(&devices[i]);
+
+    /* On error, try next device */
+    if (mtp_device == NULL)
+      continue;
+
+    /* Add the device to the list */
+    mtp_device->next = NULL;
+    if (mtp_device_list == NULL) {
+      mtp_device_list = current_device = mtp_device;
+    } else {
+      current_device->next = mtp_device;
+      current_device = mtp_device;
+    }
+  }
+  return mtp_device_list;
+}
+
+/**
+ * Get the number of devices that are available in the listed device list
+ * @param device_list Pointer to a linked list of devices
+ * @return Number of devices in the device list device_list
+ * @see LIBMTP_Get_Connected_Devices()
+ */ 
+uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *device_list)
+{
+  uint32_t numdevices = 0;
+  LIBMTP_mtpdevice_t *iter;
+  for(iter = device_list; iter != NULL; iter = iter->next)
+    numdevices++;
+  
+  return numdevices;
+}
+
+/**
+ * Get the first connected MTP device node in the linked list of devices.
+ * Currently this only provides access to USB devices
+ * @param device_list A list of devices ready to be used by the caller. You
+ *        need to know how many there are.
+ * @return Any error information gathered from device connections
+ * @see LIBMTP_Number_Devices_In_List()
+ */
+LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_list)
+{
+  LIBMTP_raw_device_t *devices;
+  int numdevs;
+  LIBMTP_error_number_t ret;
+  
+  ret = LIBMTP_Detect_Raw_Devices(&devices, &numdevs);
+  if (ret != LIBMTP_ERROR_NONE) {
+    *device_list = NULL;
+    return ret;
+  }
+
+  /* Assign linked list of devices */
+  if (devices == NULL || numdevs == 0) {
+    *device_list = NULL;
+    return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
+  }
+
+  *device_list = create_usb_mtp_devices(devices, numdevs);
+  free(devices);
+
+  /* TODO: Add wifi device access here */
+  
+  /* We have found some devices but create failed */
+  if (*device_list == NULL)
+    return LIBMTP_ERROR_CONNECTING;
+
+  return LIBMTP_ERROR_NONE;
+}
+
+/**
+ * This closes and releases an allocated MTP device.
+ * @param device a pointer to the MTP device to release.
+ */
+void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t *device)
+{
+  if(device != NULL)
+  {
+    if(device->next != NULL)
+    {
+      LIBMTP_Release_Device_List(device->next);
+    }
+    
+    LIBMTP_Release_Device(device);
+  }
+}
+
+/**
+ * This closes and releases an allocated MTP device.
+ * @param device a pointer to the MTP device to release.
+ */
+void LIBMTP_Release_Device(LIBMTP_mtpdevice_t *device)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+
+  close_device(ptp_usb, params);
+  // Clear error stack
+  LIBMTP_Clear_Errorstack(device);
+  // Free iconv() converters...
+  iconv_close(params->cd_locale_to_ucs2);
+  iconv_close(params->cd_ucs2_to_locale);
+  free(ptp_usb);  
+  ptp_free_params(params);
+  free_storage_list(device);
+  free(device);
+}
+
+/**
+ * This can be used by any libmtp-intrinsic code that
+ * need to stack up an error on the stack. You are only
+ * supposed to add errors to the error stack using this
+ * function, do not create and reference error entries
+ * directly.
+ */
+static void add_error_to_errorstack(LIBMTP_mtpdevice_t *device,
+				    LIBMTP_error_number_t errornumber,
+				    char const * const error_text)
+{
+  LIBMTP_error_t *newerror;
+  
+  if (device == NULL) {
+    fprintf(stderr, "LIBMTP PANIC: Trying to add error to a NULL device!\n");
+    return;
+  }
+  newerror = (LIBMTP_error_t *) malloc(sizeof(LIBMTP_error_t));
+  newerror->errornumber = errornumber;
+  newerror->error_text = strdup(error_text);
+  newerror->next = NULL;
+  if (device->errorstack == NULL) {
+    device->errorstack = newerror;
+  } else {
+    LIBMTP_error_t *tmp = device->errorstack;
+    
+    while (tmp->next != NULL) {
+      tmp = tmp->next;
+    }
+    tmp->next = newerror;
+  }
+}
+
+/**
+ * Adds an error from the PTP layer to the error stack.
+ */
+static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
+					uint16_t ptp_error,
+					char const * const error_text)
+{
+  if (device == NULL) {
+    fprintf(stderr, "LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
+    return;
+  } else {
+    char outstr[256];
+    snprintf(outstr, sizeof(outstr), "PTP Layer error %04x: %s", ptp_error, error_text);
+    outstr[sizeof(outstr)-1] = '\0';
+    add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
+    add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, "(Look this up in ptp.h for an explanation.)");
+  }
+}
+
+/**
+ * This returns the error stack for a device in case you
+ * need to either reference the error numbers (e.g. when
+ * creating multilingual apps with multiple-language text
+ * representations for each error number) or when you need
+ * to build a multi-line error text widget or something like
+ * that. You need to call the <code>LIBMTP_Clear_Errorstack</code>
+ * to clear it when you're finished with it.
+ * @param device a pointer to the MTP device to get the error
+ *        stack for.
+ * @return the error stack or NULL if there are no errors
+ *         on the stack.
+ * @see LIBMTP_Clear_Errorstack()
+ * @see LIBMTP_Dump_Errorstack()
+ */
+LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t *device)
+{
+  if (device == NULL) {
+    fprintf(stderr, "LIBMTP PANIC: Trying to get the error stack of a NULL device!\n");
+    return NULL;
+  }
+  return device->errorstack;
+}
+
+/**
+ * This function clears the error stack of a device and frees
+ * any memory used by it. Call this when you're finished with
+ * using the errors.
+ * @param device a pointer to the MTP device to clear the error
+ *        stack for.
+ */
+void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t *device)
+{
+  if (device == NULL) {
+    fprintf(stderr, "LIBMTP PANIC: Trying to clear the error stack of a NULL device!\n");
+  } else {
+    LIBMTP_error_t *tmp = device->errorstack;
+  
+    while (tmp != NULL) {
+      LIBMTP_error_t *tmp2;
+      
+      if (tmp->error_text != NULL) {
+	free(tmp->error_text);
+      }
+      tmp2 = tmp;
+      tmp = tmp->next;
+      free(tmp2);
+    }
+    device->errorstack = NULL;
+  }
+}
+
+/**
+ * This function dumps the error stack to <code>stderr</code>.
+ * (You still have to clear the stack though.)
+ * @param device a pointer to the MTP device to dump the error
+ *        stack for.
+ */
+void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t *device)
+{
+  if (device == NULL) {
+    fprintf(stderr, "LIBMTP PANIC: Trying to dump the error stack of a NULL device!\n");
+  } else {
+    LIBMTP_error_t *tmp = device->errorstack;
+
+    while (tmp != NULL) {
+      if (tmp->error_text != NULL) {
+	fprintf(stderr, "Error %d: %s\n", tmp->errornumber, tmp->error_text);
+      } else {
+	fprintf(stderr, "Error %d: (unknown)\n", tmp->errornumber);
+      }
+      tmp = tmp->next;
+    }
+  }
+}
+
+void LIBMTP_Set_Device_Timeout(LIBMTP_mtpdevice_t *device, int milliseconds)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  set_usb_device_timeout(ptp_usb, milliseconds);
+}
+
+void LIBMTP_Get_Device_Timeout(LIBMTP_mtpdevice_t *device, int * milliseconds)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  get_usb_device_timeout(ptp_usb, milliseconds);
+}
+
+/**
+ * This command gets all handles and stuff by FAST directory retrieveal 
+ * which is available by getting all metadata for object 
+ * <code>0xffffffff</code> which simply means "all metadata for all objects".
+ * This works on the vast majority of MTP devices (there ARE exceptions!)
+ * and is quite quick. Check the error stack to see if there were
+ * problems getting the metadata.
+ * @return 0 if all was OK, -1 on failure.
+ */
+static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device,
+				 uint32_t storage)
+{
+  PTPParams      *params = (PTPParams *) device->params;
+  int		 cnt = 0;
+  int            i, j, nrofprops;
+  uint32_t	 lasthandle = 0xffffffff;
+  MTPProperties  *props = NULL;
+  MTPProperties  *prop;
+  uint16_t       ret;
+  int            oldtimeout;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+
+  /* The follow request causes the device to generate
+   * a list of very file on the device and return it
+   * in a single response.
+   *
+   * Some slow devices as well as devices with very
+   * large file systems can easily take longer then
+   * the standard timeout value before it is able
+   * to return a response.
+   *
+   * Temporarly set timeout to allow working with
+   * widest range of devices.
+   */
+  get_usb_device_timeout(ptp_usb, &oldtimeout);
+  set_usb_device_timeout(ptp_usb, 60000);
+  
+  ret = ptp_mtp_getobjectproplist(params, 0xffffffff, &props, &nrofprops);
+  set_usb_device_timeout(ptp_usb, oldtimeout);
+
+  if (ret == PTP_RC_MTP_Specification_By_Group_Unsupported) {
+    // What's the point in the device implementing this command if 
+    // you cannot use it to get all props for AT LEAST one object?
+    // Well, whatever...
+    add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
+    "cannot retrieve all metadata for an object on this device.");
+    return -1;
+  }
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): "
+    "could not get proplist of all objects.");
+    return -1;
+  }
+  if (props == NULL && nrofprops != 0) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
+			    "get_all_metadata_fast(): "
+			    "call to ptp_mtp_getobjectproplist() returned "
+			    "inconsistent results.");
+    return -1;
+  }
+  /* 
+   * We count the number of objects by counting the ObjectHandle
+   * references, whenever it changes we get a new object, when it's
+   * the same, it is just different properties of the same object.
+   */
+  prop = props;
+  for (i=0;i<nrofprops;i++) {
+      if (lasthandle != prop->ObjectHandle) {
+	cnt++;
+	lasthandle = prop->ObjectHandle;
+      }
+      prop++;
+  }
+  lasthandle = 0xffffffff;
+  params->objects = calloc (sizeof(PTPObject),cnt);
+  prop = props;
+  i = -1;
+  for (j=0;j<nrofprops;j++) {
+    if (lasthandle != prop->ObjectHandle) {
+      if (i >= 0) {
+        params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
+	if (!params->objects[i].oi.Filename) {
+	  /* I have one such file on my Creative (Marcus) */
+	  params->objects[i].oi.Filename = strdup("<null>");
+	}
+      }
+      i++;
+      lasthandle = prop->ObjectHandle;
+      params->objects[i].oid = prop->ObjectHandle;
+    }
+    switch (prop->property) {
+    case PTP_OPC_ParentObject:
+      params->objects[i].oi.ParentObject = prop->propval.u32;
+      params->objects[i].flags |= PTPOBJECT_PARENTOBJECT_LOADED;
+      break;
+    case PTP_OPC_ObjectFormat:
+      params->objects[i].oi.ObjectFormat = prop->propval.u16;
+      break;
+    case PTP_OPC_ObjectSize:
+      // We loose precision here, up to 32 bits! However the commands that
+      // retrieve metadata for files and tracks will make sure that the
+      // PTP_OPC_ObjectSize is read in and duplicated again.
+      if (device->object_bitsize == 64) {
+	params->objects[i].oi.ObjectCompressedSize = (uint32_t) prop->propval.u64;
+      } else {
+	params->objects[i].oi.ObjectCompressedSize = prop->propval.u32;
+      }
+      break;
+    case PTP_OPC_StorageID:
+      params->objects[i].oi.StorageID = prop->propval.u32;
+      params->objects[i].flags |= PTPOBJECT_STORAGEID_LOADED;
+      break;
+    case PTP_OPC_ObjectFileName:
+      if (prop->propval.str != NULL)
+        params->objects[i].oi.Filename = strdup(prop->propval.str);
+      break;
+    default: {
+      MTPProperties *newprops;
+
+      /* Copy all of the other MTP oprierties into the per-object proplist */
+      if (params->objects[i].nrofmtpprops) {
+        newprops = realloc(params->objects[i].mtpprops,(params->objects[i].nrofmtpprops+1)*sizeof(MTPProperties));
+      } else {
+        newprops = calloc(sizeof(MTPProperties),1);
+      }
+      if (!newprops) return 0; /* FIXME: error handling? */
+      params->objects[i].mtpprops = newprops;
+      memcpy(&params->objects[i].mtpprops[params->objects[i].nrofmtpprops],&props[j],sizeof(props[j]));
+      params->objects[i].nrofmtpprops++;
+      params->objects[i].flags |= PTPOBJECT_MTPPROPLIST_LOADED;
+      break;
+    }
+    }
+    prop++;
+  }
+  /* mark last entry also */
+  params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
+  params->nrofobjects = i+1;
+  /* The device might not give the list in linear ascending order */
+  ptp_objects_sort (params);
+  return 0;
+}
+
+/**
+ * This function will recurse through all the directories on the device,
+ * starting at the root directory, gathering metadata as it moves along.
+ * It works better on some devices that will only return data for a
+ * certain directory and does not respect the option to get all metadata
+ * for all objects.
+ */
+static void get_handles_recursively(LIBMTP_mtpdevice_t *device, 
+				    PTPParams *params, 
+				    uint32_t storageid,
+				    uint32_t parent)
+{
+  PTPObjectHandles currentHandles;
+  int i = 0;
+  uint16_t ret = ptp_getobjecthandles(params,
+                                      storageid,
+                                      PTP_GOH_ALL_FORMATS,
+                                      parent,
+                                      &currentHandles);
+  
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "get_handles_recursively(): could not get object handles.");
+    return;
+  }
+  
+  if (currentHandles.Handler == NULL || currentHandles.n == 0)
+    return;
+
+  // Now descend into any subdirectories found
+  for (i = 0; i < currentHandles.n; i++) {    
+    PTPObject *ob;
+    ret = ptp_object_want(params,currentHandles.Handler[i],PTPOBJECT_OBJECTINFO_LOADED, &ob);
+    if (ret == PTP_RC_OK) {
+      if (ob->oi.ObjectFormat == PTP_OFC_Association)
+        get_handles_recursively(device, params, storageid, currentHandles.Handler[i]);
+    } else {
+      add_error_to_errorstack(device,
+			      LIBMTP_ERROR_CONNECTING,
+			      "Found a bad handle, trying to ignore it.");
+    }
+  }
+  free(currentHandles.Handler);
+}
+
+
+LIBMTP_file_t * obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  PTPParams *params = (PTPParams *) device->params;
+  LIBMTP_file_t *file;
+  PTPObject *xob;
+  uint16_t ret;
+
+
+  if (ob->oi.Filename == NULL)
+    ob->oi.Filename = strdup("<null>");
+
+  if (ob->oi.Keywords == NULL)
+    ob->oi.Keywords = strdup("<null>");
+
+  // Allocate a new file type
+  file = LIBMTP_new_file_t();
+
+  file->parent_id = ob->oi.ParentObject;
+  file->storage_id = ob->oi.StorageID;
+
+  // This is some sort of unique ID so we can keep track of the track.
+  file->item_id = ob->oid;
+
+    // Set the filetype
+  file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+
+  // Set the modification date
+  file->modificationdate = ob->oi.ModificationDate;
+
+  // Original file-specific properties
+  // We only have 32-bit file size here; if we find it, we use the
+  // PTP_OPC_ObjectSize property which has 64bit precision.
+  file->filesize = ob->oi.ObjectCompressedSize;
+  if (ob->oi.Filename != NULL) {
+      file->filename = strdup(ob->oi.Filename);
+  }
+
+  /*
+  * A special quirk for devices that doesn't quite
+  * remember that some files marked as "unknown" type are
+  * actually OGG or FLAC files. We look at the filename extension
+  * and see if it happens that this was atleast named "ogg" or "flac"
+  * and fall back on this heuristic approach in that case,
+  * for these bugged devices only.
+  */
+  if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
+    if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
+        FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+        has_ogg_extension(file->filename))
+
+      file->filetype = LIBMTP_FILETYPE_OGG;
+
+      if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && has_flac_extension(file->filename))
+        file->filetype = LIBMTP_FILETYPE_FLAC;
+    }
+
+  /*
+  * If we have a cached, large set of metadata, then use it!
+  */
+  ret = ptp_object_want (params, ob->oid, PTPOBJECT_MTPPROPLIST_LOADED, &xob);
+  if (ob->mtpprops) {
+    MTPProperties *prop = ob->mtpprops;
+    int i;
+
+    for (i=0;i<ob->nrofmtpprops;i++) {
+      // Pick ObjectSize here...
+      if (prop->property == PTP_OPC_ObjectSize) {
+        if (device->object_bitsize == 64) {
+          file->filesize = prop->propval.u64;
+        } else {
+          file->filesize = prop->propval.u32;
+        }
+        break;
+      }
+      prop++;
+    }
+  } else {
+    uint16_t *props = NULL;
+    uint32_t propcnt = 0;
+
+    // First see which properties can be retrieved for this object format
+    ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "obj2file(): call to ptp_mtp_getobjectpropssupported() failed.");
+      // Silently fall through.
+    } else {
+      int i;
+      for (i=0;i<propcnt;i++) {
+
+/*
+    TODO: (yavor) See what is a sensible thing to do for Folders
+    if (ob->oi.ObjectFormat == PTP_OFC_Association)
+*/
+      switch (props[i]) {
+        case PTP_OPC_ObjectSize:
+          if (device->object_bitsize == 64) {
+            file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+          } else {
+            file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+          }
+          break;
+        default:
+            break;
+        }
+      }
+      free(props);
+    }
+  }
+
+  return file;
+}
+
+
+
+static LIBMTP_file_t * get_files(LIBMTP_mtpdevice_t *device,
+                        PTPParams *params,
+                        uint32_t storageid,
+                        uint32_t parentId
+                        )
+{
+  int i = 0;
+  LIBMTP_file_t *curfile = NULL;
+  LIBMTP_file_t *retfiles = NULL;
+  PTPObjectHandles currentHandles;
+
+  uint16_t ret = ptp_getobjecthandles(params,
+                                      storageid,
+                                      PTP_GOH_ALL_FORMATS,
+                                      parentId,
+                                      &currentHandles);
+
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "get_files(): could not get object handles.");
+    return NULL;
+  }
+
+  if (currentHandles.Handler == NULL || currentHandles.n == 0)
+    return NULL;
+
+  for (i = 0; i < currentHandles.n; i++) {
+    PTPObject *ob;
+    LIBMTP_file_t *file;
+
+    ret = ptp_object_want(params, currentHandles.Handler[i],PTPOBJECT_OBJECTINFO_LOADED, &ob);
+    if (ret != PTP_RC_OK)
+      return NULL;
+
+    file = obj2file(device, ob);
+
+    if (file == NULL)
+      continue;
+
+    // Add track to a list that will be returned afterwards.
+    if (curfile == NULL) {
+      curfile = file;
+      retfiles = file;
+    } else {
+      curfile->next = file;
+      curfile = file;
+    }
+ }
+
+  free(currentHandles.Handler);
+
+  // Return a pointer to the original first file
+  // in the big list.
+  return retfiles;
+}
+
+/**
+ * This function controls the usage of the internal object cache.
+ * The default configuration loads all object handles on initialization.
+ * In order to handle large number of files turn on the on demand
+ * loading by calling this function with parameter 1, and use
+ * LIBMTP_Get_Files_And_Folders() to load content when needed.
+ *
+ * @param flag - 0 means turn off on demand loading.
+ *             - 1 means turn on on demand loading.
+ */
+void LIBMTP_Set_Load_Cache_On_Demand(int flag)
+{
+    load_cache_on_demand = flag;
+}
+
+/**
+ * This function retrieves the content of a folder with id - parentId.
+ * The result contains both files and folders.
+ * NOTE: the request will always perform I/O with the device.
+ * @param device a pointer to the MTP device to report info from.
+ * @storageId the id for the storage.
+ * @param parentId the parent folder id.
+ */
+LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device, uint32_t storageId, uint32_t parentId)
+{
+  LIBMTP_file_t *retfiles = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  int ret;
+  uint32_t i;
+
+#if 0
+  //TODO: (yavor) Try to use get_all_metadata_fast for a parendId.
+  if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
+      && !FLAG_BROKEN_MTPGETOBJPROPLIST(ptp_usb)
+      && !FLAG_BROKEN_MTPGETOBJPROPLIST_ALL(ptp_usb)) {
+    // Use the fast method. Ignore return value for now.
+    ret = get_all_metadata_fast(device, PTP_GOH_ALL_STORAGE);
+  }
+#endif
+
+
+  retfiles = get_files(device, params, storageId, parentId);
+
+  return retfiles;
+}
+
+/**
+ * This function refresh the internal handle list whenever
+ * the items stored inside the device is altered. On operations
+ * that do not add or remove objects, this is typically not
+ * called.
+ * @param device a pointer to the MTP device to flush handles for.
+ */
+static void flush_handles(LIBMTP_mtpdevice_t *device)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  int ret;
+  uint32_t i;
+
+  if (load_cache_on_demand) {
+    return;
+  }
+
+  if (params->objects != NULL) {
+    for (i=0;i<params->nrofobjects;i++)
+      ptp_free_object (&params->objects[i]);
+    free(params->objects);
+    params->objects = NULL;
+    params->nrofobjects = 0;
+  }
+
+  if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)
+      && !FLAG_BROKEN_MTPGETOBJPROPLIST(ptp_usb)
+      && !FLAG_BROKEN_MTPGETOBJPROPLIST_ALL(ptp_usb)) {
+    // Use the fast method. Ignore return value for now.
+    ret = get_all_metadata_fast(device, PTP_GOH_ALL_STORAGE);
+  }
+
+  // If the previous failed or returned no objects, use classic
+  // methods instead.
+  if (params->nrofobjects == 0) {
+    // Get all the handles using just standard commands.
+    if (device->storage == NULL) {
+      get_handles_recursively(device, params,
+			      PTP_GOH_ALL_STORAGE,
+			      PTP_GOH_ROOT_PARENT);
+    } else {
+      // Get handles for each storage in turn.
+      LIBMTP_devicestorage_t *storage = device->storage;
+      while(storage != NULL) {
+	get_handles_recursively(device, params,
+				storage->id,
+				PTP_GOH_ROOT_PARENT);
+	storage = storage->next;
+      }
+    }
+  }
+  
+  /*
+   * Loop over the handles, fix up any NULL filenames or
+   * keywords, then attempt to locate some default folders
+   * in the root directory of the primary storage.
+   */
+  for(i = 0; i < params->nrofobjects; i++) {
+    PTPObject *ob, *xob;
+ 
+    ob = &params->objects[i];
+    ret = ptp_object_want(params,params->objects[i].oid,PTPOBJECT_OBJECTINFO_LOADED, &xob);
+    if (ret != PTP_RC_OK) {
+	fprintf(stderr,"broken! %x not found\n", params->objects[i].oid);
+    }
+    if (ob->oi.Filename == NULL)
+      ob->oi.Filename = strdup("<null>");
+    if (ob->oi.Keywords == NULL)
+      ob->oi.Keywords = strdup("<null>");
+
+    /* Ignore handles that point to non-folders */
+    if(ob->oi.ObjectFormat != PTP_OFC_Association)
+      continue;
+    /* Only look in the root folder */
+    if (ob->oi.ParentObject != 0x00000000U)
+      continue;
+    /* Only look in the primary storage */
+    if (device->storage != NULL && ob->oi.StorageID != device->storage->id)
+      continue;
+
+    
+    /* Is this the Music Folder */
+    if (!strcasecmp(ob->oi.Filename, "My Music") ||
+	!strcasecmp(ob->oi.Filename, "Music")) {
+      device->default_music_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "My Playlists") ||
+	     !strcasecmp(ob->oi.Filename, "Playlists")) {
+      device->default_playlist_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "My Pictures") ||
+	     !strcasecmp(ob->oi.Filename, "Pictures")) {
+      device->default_picture_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "My Video") ||
+	     !strcasecmp(ob->oi.Filename, "Video")) {
+	device->default_video_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "My Organizer")) {
+      device->default_organizer_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "ZENcast") ||
+	     !strcasecmp(ob->oi.Filename, "Datacasts")) {
+      device->default_zencast_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "My Albums") ||
+	     !strcasecmp(ob->oi.Filename, "Albums")) {
+      device->default_album_folder = ob->oid;
+    }
+    else if (!strcasecmp(ob->oi.Filename, "Text") ||
+	     !strcasecmp(ob->oi.Filename, "Texts")) {
+      device->default_text_folder = ob->oid;
+    }
+  }
+}
+
+/**
+ * This function traverses a devices storage list freeing up the 
+ * strings and the structs.
+ * @param device a pointer to the MTP device to free the storage 
+ * list for.
+ */
+static void free_storage_list(LIBMTP_mtpdevice_t *device)
+{
+  LIBMTP_devicestorage_t *storage;
+  LIBMTP_devicestorage_t *tmp;
+
+  storage = device->storage;
+  while(storage != NULL) {  
+    if (storage->StorageDescription != NULL) {
+      free(storage->StorageDescription);
+    }
+    if (storage->VolumeIdentifier != NULL) {
+      free(storage->VolumeIdentifier);
+    }
+    tmp = storage;
+    storage = storage->next;
+    free(tmp);
+  }
+  device->storage = NULL;
+
+  return;
+}
+
+/**
+ * This function traverses a devices storage list freeing up the
+ * strings and the structs.
+ * @param device a pointer to the MTP device to free the storage
+ * list for.
+ */
+static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
+{
+  LIBMTP_devicestorage_t *oldhead, *ptr1, *ptr2, *newlist;
+
+  if (device->storage == NULL)
+    return -1;
+  if (sortby == LIBMTP_STORAGE_SORTBY_NOTSORTED) 
+    return 0;
+
+  oldhead = ptr1 = ptr2 = device->storage;
+
+  newlist = NULL;
+
+  while(oldhead != NULL) {
+    ptr1 = ptr2 = oldhead;
+    while(ptr1 != NULL) {
+
+      if (sortby == LIBMTP_STORAGE_SORTBY_FREESPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes) 
+        ptr2 = ptr1;
+      if (sortby == LIBMTP_STORAGE_SORTBY_MAXSPACE && ptr1->FreeSpaceInBytes > ptr2->FreeSpaceInBytes) 
+        ptr2 = ptr1;
+
+      ptr1 = ptr1->next;
+    }
+
+    // Make our previous entries next point to our next
+    if(ptr2->prev != NULL) {
+      ptr1 = ptr2->prev;
+      ptr1->next = ptr2->next; 
+    } else {
+      oldhead = ptr2->next;
+      if(oldhead != NULL)
+        oldhead->prev = NULL;
+    }
+
+    // Make our next entries previous point to our previous
+    ptr1 = ptr2->next;
+    if(ptr1 != NULL) {
+      ptr1->prev = ptr2->prev;
+    } else {
+      ptr1 = ptr2->prev;
+      if(ptr1 != NULL)
+        ptr1->next = NULL;
+    }
+  
+    if(newlist == NULL) {
+      newlist = ptr2;
+      newlist->prev = NULL;
+    } else {
+      ptr2->prev = newlist;
+      newlist->next = ptr2;
+      newlist = newlist->next;
+    }
+  }
+ 
+  if (newlist != NULL) {
+    newlist->next = NULL;
+    while(newlist->prev != NULL) 
+      newlist = newlist->prev;
+    device->storage = newlist;
+  }
+
+  return 0;
+}
+
+/**
+ * This function grabs the first writeable storageid from the 
+ * device storage list.
+ * @param device a pointer to the MTP device to locate writeable 
+ *        storage for.
+ * @param fitsize a file of this file must fit on the device.
+ */
+static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fitsize)
+{
+  LIBMTP_devicestorage_t *storage;
+  uint32_t store = 0x00000000; //Should this be 0xffffffffu instead?
+  int subcall_ret;
+
+  // See if there is some storage we can fit this file on.
+  storage = device->storage;
+  if (storage == NULL) {
+    // Sometimes the storage just cannot be detected.
+    store = 0x00000000U;
+  } else {
+    while(storage != NULL) {
+      // These storages cannot be used.
+      if (storage->StorageType == PTP_ST_FixedROM || storage->StorageType == PTP_ST_RemovableROM) {
+	storage = storage->next;
+	continue;
+      }
+      // Storage IDs with the lower 16 bits 0x0000 are not supposed
+      // to be writeable.
+      if ((storage->id & 0x0000FFFFU) == 0x00000000U) {
+	storage = storage->next;
+	continue;
+      }
+      // Also check the access capability to avoid e.g. deletable only storages
+      if (storage->AccessCapability == PTP_AC_ReadOnly || storage->AccessCapability == PTP_AC_ReadOnly_with_Object_Deletion) {
+	storage = storage->next;
+	continue;
+      }
+      // Then see if we can fit the file.
+      subcall_ret = check_if_file_fits(device, storage, fitsize);
+      if (subcall_ret != 0) {
+	storage = storage->next;
+      } else {
+	// We found a storage that is writable and can fit the file!
+	break;
+      }
+    }
+    if (storage == NULL) {
+      add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL, "LIBMTP_Send_File_From_File_Descriptor(): " 
+			      "all device storage is full or corrupt.");
+      return -1;
+    }
+    store = storage->id;
+  }
+
+  return store;
+}
+
+/**
+ * This function grabs the freespace from a certain storage in
+ * device storage list.
+ * @param device a pointer to the MTP device to free the storage
+ * list for.
+ * @param storageid the storage ID for the storage to flush and
+ * get free space for.
+ * @param freespace the free space on this storage will be returned
+ * in this variable.
+ */
+static int get_storage_freespace(LIBMTP_mtpdevice_t *device, 
+				 LIBMTP_devicestorage_t *storage,
+				 uint64_t *freespace)
+{
+  PTPParams *params = (PTPParams *) device->params;
+
+  // Always query the device about this, since some models explicitly
+  // needs that. We flush all data on queries storage here.
+  if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
+    PTPStorageInfo storageInfo;
+    uint16_t ret;
+    
+    ret = ptp_getstorageinfo(params, storage->id, &storageInfo);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "get_first_storage_freespace(): could not get storage info.");
+      return -1;
+    }
+    if (storage->StorageDescription != NULL) {
+      free(storage->StorageDescription);
+    }
+    if (storage->VolumeIdentifier != NULL) {
+      free(storage->VolumeIdentifier);
+    }
+    storage->StorageType = storageInfo.StorageType;
+    storage->FilesystemType = storageInfo.FilesystemType;
+    storage->AccessCapability = storageInfo.AccessCapability;
+    storage->MaxCapacity = storageInfo.MaxCapability;
+    storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
+    storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
+    storage->StorageDescription = storageInfo.StorageDescription;
+    storage->VolumeIdentifier = storageInfo.VolumeLabel;
+  }
+  if(storage->FreeSpaceInBytes == (uint64_t) -1)
+    return -1;
+  *freespace = storage->FreeSpaceInBytes;
+  return 0;
+}
+
+/**
+ * This function dumps out a large chunk of textual information
+ * provided from the PTP protocol and additionally some extra
+ * MTP-specific information where applicable.
+ * @param device a pointer to the MTP device to report info from.
+ */
+void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
+{
+  int i;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  LIBMTP_devicestorage_t *storage = device->storage;
+
+  printf("USB low-level info:\n");
+  dump_usbinfo(ptp_usb);
+  /* Print out some verbose information */
+  printf("Device info:\n");
+  printf("   Manufacturer: %s\n", params->deviceinfo.Manufacturer);
+  printf("   Model: %s\n", params->deviceinfo.Model);
+  printf("   Device version: %s\n", params->deviceinfo.DeviceVersion);
+  printf("   Serial number: %s\n", params->deviceinfo.SerialNumber);
+  printf("   Vendor extension ID: 0x%08x\n", params->deviceinfo.VendorExtensionID);
+  printf("   Vendor extension description: %s\n", params->deviceinfo.VendorExtensionDesc);
+  printf("   Detected object size: %d bits\n", device->object_bitsize);
+  printf("Supported operations:\n");
+  for (i=0;i<params->deviceinfo.OperationsSupported_len;i++) {
+    char txt[256];
+
+    (void) ptp_render_opcode (params, params->deviceinfo.OperationsSupported[i], sizeof(txt), txt);
+    printf("   %04x: %s\n", params->deviceinfo.OperationsSupported[i], txt);
+  }
+  printf("Events supported:\n");
+  if (params->deviceinfo.EventsSupported_len == 0) {
+    printf("   None.\n");
+  } else {
+    for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
+      printf("   0x%04x\n", params->deviceinfo.EventsSupported[i]);
+    }
+  }
+  printf("Device Properties Supported:\n");
+  for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++) {
+    char const *propdesc = ptp_get_property_description(params, params->deviceinfo.DevicePropertiesSupported[i]);
+
+    if (propdesc != NULL) {
+      printf("   0x%04x: %s\n", params->deviceinfo.DevicePropertiesSupported[i], propdesc);
+    } else {
+      uint16_t prop = params->deviceinfo.DevicePropertiesSupported[i];
+      printf("   0x%04x: Unknown property\n", prop);
+    }
+  }
+
+  if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported)) {
+    printf("Playable File (Object) Types and Object Properties Supported:\n");
+    for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
+      char txt[256];
+      uint16_t ret;
+      uint16_t *props = NULL;
+      uint32_t propcnt = 0;
+      int j;
+
+      (void) ptp_render_ofc (params, params->deviceinfo.ImageFormats[i], sizeof(txt), txt);
+      printf("   %04x: %s\n", params->deviceinfo.ImageFormats[i], txt);
+
+      ret = ptp_mtp_getobjectpropssupported (params, params->deviceinfo.ImageFormats[i], &propcnt, &props);
+      if (ret != PTP_RC_OK) {
+	add_ptp_error_to_errorstack(device, ret, "LIBMTP_Dump_Device_Info(): error on query for object properties.");
+      } else {
+	for (j=0;j<propcnt;j++) {
+	  PTPObjectPropDesc opd;
+	  int k;
+	  
+	  printf("      %04x: %s", props[j], LIBMTP_Get_Property_Description(map_ptp_property_to_libmtp_property(props[j])));
+	  // Get a more verbose description
+	  ret = ptp_mtp_getobjectpropdesc(params, props[j], params->deviceinfo.ImageFormats[i], &opd);
+	  if (ret != PTP_RC_OK) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Dump_Device_Info(): "
+				    "could not get property description.");
+	    break;
+	  }
+
+	  if (opd.DataType == PTP_DTC_STR) {
+	    printf(" STRING data type");
+	    switch (opd.FormFlag) {
+	    case PTP_OPFF_DateTime:
+	      printf(" DATETIME FORM");
+	      break;
+	    case PTP_OPFF_RegularExpression:
+	      printf(" REGULAR EXPRESSION FORM");
+	      break;
+	    case PTP_OPFF_LongString:
+	      printf(" LONG STRING FORM");
+	      break;
+	    default:
+	      break;
+	    }
+	  } else {
+	    if (opd.DataType & PTP_DTC_ARRAY_MASK) {
+	      printf(" array of");
+	    }
+
+	    switch (opd.DataType & (~PTP_DTC_ARRAY_MASK)) {
+
+	    case PTP_DTC_UNDEF:
+	      printf(" UNDEFINED data type");
+	      break;
+	      
+	    case PTP_DTC_INT8:
+	      printf(" INT8 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+		printf(" range: MIN %d, MAX %d, STEP %d",
+		       opd.FORM.Range.MinimumValue.i8,
+		       opd.FORM.Range.MaximumValue.i8,
+		       opd.FORM.Range.StepSize.i8);
+		break;
+	      case PTP_OPFF_Enumeration:
+		printf(" enumeration: ");
+		for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		  printf("%d, ", opd.FORM.Enum.SupportedValue[k].i8);
+		}
+		break;
+	      case PTP_OPFF_ByteArray:
+		printf(" byte array: ");
+		break;
+	      default:
+		printf(" ANY 8BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_UINT8:
+	      printf(" UINT8 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+		printf(" range: MIN %d, MAX %d, STEP %d",
+		       opd.FORM.Range.MinimumValue.u8,
+		       opd.FORM.Range.MaximumValue.u8,
+		       opd.FORM.Range.StepSize.u8);
+		break;
+	      case PTP_OPFF_Enumeration:
+		printf(" enumeration: ");
+		for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		  printf("%d, ", opd.FORM.Enum.SupportedValue[k].u8);
+		}
+		break;
+	      case PTP_OPFF_ByteArray:
+		printf(" byte array: ");
+		break;
+	      default:
+		printf(" ANY 8BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_INT16:
+	      printf(" INT16 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+	      printf(" range: MIN %d, MAX %d, STEP %d",
+		     opd.FORM.Range.MinimumValue.i16,
+		     opd.FORM.Range.MaximumValue.i16,
+		     opd.FORM.Range.StepSize.i16);
+	      break;
+	      case PTP_OPFF_Enumeration:
+		printf(" enumeration: ");
+		for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		  printf("%d, ", opd.FORM.Enum.SupportedValue[k].i16);
+		}
+		break;
+	      default:
+		printf(" ANY 16BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_UINT16:
+	      printf(" UINT16 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+		printf(" range: MIN %d, MAX %d, STEP %d",
+		       opd.FORM.Range.MinimumValue.u16,
+		       opd.FORM.Range.MaximumValue.u16,
+		       opd.FORM.Range.StepSize.u16);
+		break;
+	      case PTP_OPFF_Enumeration:
+		printf(" enumeration: ");
+		for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		  printf("%d, ", opd.FORM.Enum.SupportedValue[k].u16);
+		}
+		break;
+	      default:
+		printf(" ANY 16BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_INT32:
+	      printf(" INT32 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+		printf(" range: MIN %d, MAX %d, STEP %d",
+		       opd.FORM.Range.MinimumValue.i32,
+		       opd.FORM.Range.MaximumValue.i32,
+		       opd.FORM.Range.StepSize.i32);
+		break;
+	      case PTP_OPFF_Enumeration:
+		printf(" enumeration: ");
+		for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		  printf("%d, ", opd.FORM.Enum.SupportedValue[k].i32);
+		}
+		break;
+	      default:
+		printf(" ANY 32BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_UINT32:
+	      printf(" UINT32 data type");
+	      switch (opd.FormFlag) {
+	      case PTP_OPFF_Range:
+		printf(" range: MIN %d, MAX %d, STEP %d",
+		       opd.FORM.Range.MinimumValue.u32,
+		       opd.FORM.Range.MaximumValue.u32,
+		       opd.FORM.Range.StepSize.u32);
+		break;
+	      case PTP_OPFF_Enumeration:
+		// Special pretty-print for FOURCC codes
+		if (params->deviceinfo.ImageFormats[i] == PTP_OPC_VideoFourCCCodec) {
+		  printf(" enumeration of u32 casted FOURCC: ");
+		  for (k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		    if (opd.FORM.Enum.SupportedValue[k].u32 == 0) {
+		      printf("ANY, ");
+		    } else {
+		      char fourcc[6];
+		      fourcc[0] = (opd.FORM.Enum.SupportedValue[k].u32 >> 24) & 0xFFU;
+		      fourcc[1] = (opd.FORM.Enum.SupportedValue[k].u32 >> 16) & 0xFFU;
+		      fourcc[2] = (opd.FORM.Enum.SupportedValue[k].u32 >> 8) & 0xFFU;
+		      fourcc[3] = opd.FORM.Enum.SupportedValue[k].u32 & 0xFFU;
+		      fourcc[4] = '\n';
+		      fourcc[5] = '\0';
+		      printf("\"%s\", ", fourcc);
+		    }
+		  }
+		} else {
+		  printf(" enumeration: ");
+		  for(k=0;k<opd.FORM.Enum.NumberOfValues;k++) {
+		    printf("%d, ", opd.FORM.Enum.SupportedValue[k].u32);
+		  }
+		}
+		break;
+	      default:
+		printf(" ANY 32BIT VALUE form");
+		break;
+	      }
+	      break;
+	      
+	    case PTP_DTC_INT64:
+	      printf(" INT64 data type");
+	      break;
+	      
+	    case PTP_DTC_UINT64:
+	      printf(" UINT64 data type");
+	      break;
+	      
+	    case PTP_DTC_INT128:
+	      printf(" INT128 data type");
+	      break;
+	      
+	    case PTP_DTC_UINT128:
+	      printf(" UINT128 data type");
+	      break;
+	      
+	    default:
+	      printf(" UNKNOWN data type");
+	      break;
+	    }
+	  }
+	  if (opd.GetSet) {
+	    printf(" GET/SET");
+	  } else {
+	    printf(" READ ONLY");
+	  }
+	  printf("\n");
+	  ptp_free_objectpropdesc(&opd);
+	}
+	free(props);
+      }
+    }
+  }
+
+  if(storage != NULL && ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
+    printf("Storage Devices:\n");
+    while(storage != NULL) {
+      printf("   StorageID: 0x%08x\n",storage->id);
+      printf("      StorageType: 0x%04x ",storage->StorageType);
+      switch (storage->StorageType) {
+      case PTP_ST_Undefined:
+	printf("(undefined)\n");
+	break;
+      case PTP_ST_FixedROM:
+	printf("fixed ROM storage\n");
+	break;
+      case PTP_ST_RemovableROM:
+	printf("removable ROM storage\n");
+	break;
+      case PTP_ST_FixedRAM:
+	printf("fixed RAM storage\n");
+	break;
+      case PTP_ST_RemovableRAM:
+	printf("removable RAM storage\n");
+	break;
+      default:
+	printf("UNKNOWN storage\n");
+	break;
+      }
+      printf("      FilesystemType: 0x%04x ",storage->FilesystemType);
+      switch(storage->FilesystemType) {
+      case PTP_FST_Undefined:
+	printf("(undefined)\n");
+	break;
+      case PTP_FST_GenericFlat:
+	printf("generic flat filesystem\n");
+	break;
+      case PTP_FST_GenericHierarchical:
+	printf("generic hierarchical\n");
+	break;
+      case PTP_FST_DCF:
+	printf("DCF\n");
+	break;
+      default:
+	printf("UNKNONWN filesystem type\n");
+	break;
+      }
+      printf("      AccessCapability: 0x%04x ",storage->AccessCapability);
+      switch(storage->AccessCapability) {
+      case PTP_AC_ReadWrite:
+	printf("read/write\n");
+	break;
+      case PTP_AC_ReadOnly:
+	printf("read only\n");
+	break;
+      case PTP_AC_ReadOnly_with_Object_Deletion:
+	printf("read only + object deletion\n");
+	break;
+      default:
+	printf("UNKNOWN access capability\n");
+	break;
+      }
+      printf("      MaxCapacity: %llu\n", (long long unsigned int) storage->MaxCapacity);
+      printf("      FreeSpaceInBytes: %llu\n", (long long unsigned int) storage->FreeSpaceInBytes);
+      printf("      FreeSpaceInObjects: %llu\n", (long long unsigned int) storage->FreeSpaceInObjects);
+      printf("      StorageDescription: %s\n",storage->StorageDescription);
+      printf("      VolumeIdentifier: %s\n",storage->VolumeIdentifier);
+      storage = storage->next;
+    }
+  }
+
+  printf("Special directories:\n");
+  printf("   Default music folder: 0x%08x\n", device->default_music_folder);
+  printf("   Default playlist folder: 0x%08x\n", device->default_playlist_folder);
+  printf("   Default picture folder: 0x%08x\n", device->default_picture_folder);
+  printf("   Default video folder: 0x%08x\n", device->default_video_folder);
+  printf("   Default organizer folder: 0x%08x\n", device->default_organizer_folder);
+  printf("   Default zencast folder: 0x%08x\n", device->default_zencast_folder);
+  printf("   Default album folder: 0x%08x\n", device->default_album_folder);
+  printf("   Default text folder: 0x%08x\n", device->default_text_folder);
+}
+
+/**
+ * This resets a device in case it supports the <code>PTP_OC_ResetDevice</code>
+ * operation code (0x1010).
+ * @param device a pointer to the device to reset.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t *device)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (!ptp_operation_issupported(params,PTP_OC_ResetDevice)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Reset_Device(): device does not support resetting.");
+    return -1;
+  }
+  ret = ptp_resetdevice(params);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "Error resetting.");
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * This retrieves the manufacturer name of an MTP device.
+ * @param device a pointer to the device to get the manufacturer name for.
+ * @return a newly allocated UTF-8 string representing the manufacturer name.
+ *         The string must be freed by the caller after use. If the call
+ *         was unsuccessful this will contain NULL.
+ */
+char *LIBMTP_Get_Manufacturername(LIBMTP_mtpdevice_t *device)
+{
+  char *retmanuf = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+
+  if (params->deviceinfo.Manufacturer != NULL) {
+    retmanuf = strdup(params->deviceinfo.Manufacturer);
+  }
+  return retmanuf;
+}
+
+/**
+ * This retrieves the model name (often equal to product name)
+ * of an MTP device.
+ * @param device a pointer to the device to get the model name for.
+ * @return a newly allocated UTF-8 string representing the model name.
+ *         The string must be freed by the caller after use. If the call
+ *         was unsuccessful this will contain NULL.
+ */
+char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t *device)
+{
+  char *retmodel = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+
+  if (params->deviceinfo.Model != NULL) {
+    retmodel = strdup(params->deviceinfo.Model);
+  }
+  return retmodel;
+}
+
+/**
+ * This retrieves the serial number of an MTP device.
+ * @param device a pointer to the device to get the serial number for.
+ * @return a newly allocated UTF-8 string representing the serial number.
+ *         The string must be freed by the caller after use. If the call
+ *         was unsuccessful this will contain NULL.
+ */
+char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t *device)
+{
+  char *retnumber = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+
+  if (params->deviceinfo.SerialNumber != NULL) {
+    retnumber = strdup(params->deviceinfo.SerialNumber);
+  }
+  return retnumber;
+}
+
+/**
+ * This retrieves the device version (hardware and firmware version) of an
+ * MTP device.
+ * @param device a pointer to the device to get the device version for.
+ * @return a newly allocated UTF-8 string representing the device version.
+ *         The string must be freed by the caller after use. If the call
+ *         was unsuccessful this will contain NULL.
+ */
+char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t *device)
+{
+  char *retversion = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+
+  if (params->deviceinfo.DeviceVersion != NULL) {
+    retversion = strdup(params->deviceinfo.DeviceVersion);
+  }
+  return retversion;
+}
+
+
+/**
+ * This retrieves the "friendly name" of an MTP device. Usually
+ * this is simply the name of the owner or something like
+ * "John Doe's Digital Audio Player". This property should be supported
+ * by all MTP devices.
+ * @param device a pointer to the device to get the friendly name for.
+ * @return a newly allocated UTF-8 string representing the friendly name.
+ *         The string must be freed by the caller after use.
+ * @see LIBMTP_Set_Friendlyname()
+ */
+char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t *device)
+{
+  PTPPropertyValue propval;
+  char *retstring = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
+    return NULL;
+  }
+
+  ret = ptp_getdevicepropvalue(params,
+			       PTP_DPC_MTP_DeviceFriendlyName,
+			       &propval,
+			       PTP_DTC_STR);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "Error getting friendlyname.");
+    return NULL;
+  }
+  if (propval.str != NULL) {
+    retstring = strdup(propval.str);
+    free(propval.str);
+  }
+  return retstring;
+}
+
+/**
+ * Sets the "friendly name" of an MTP device.
+ * @param device a pointer to the device to set the friendly name for.
+ * @param friendlyname the new friendly name for the device.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Get_Friendlyname()
+ */
+int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t *device,
+			 char const * const friendlyname)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (!ptp_property_issupported(params, PTP_DPC_MTP_DeviceFriendlyName)) {
+    return -1;
+  }
+  propval.str = (char *) friendlyname;
+  ret = ptp_setdevicepropvalue(params,
+			       PTP_DPC_MTP_DeviceFriendlyName,
+			       &propval,
+			       PTP_DTC_STR);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "Error setting friendlyname.");
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * This retrieves the syncronization partner of an MTP device. This
+ * property should be supported by all MTP devices.
+ * @param device a pointer to the device to get the sync partner for.
+ * @return a newly allocated UTF-8 string representing the synchronization
+ *         partner. The string must be freed by the caller after use.
+ * @see LIBMTP_Set_Syncpartner()
+ */
+char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t *device)
+{
+  PTPPropertyValue propval;
+  char *retstring = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
+    return NULL;
+  }
+
+  ret = ptp_getdevicepropvalue(params,
+			       PTP_DPC_MTP_SynchronizationPartner,
+			       &propval,
+			       PTP_DTC_STR);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "Error getting syncpartner.");
+    return NULL;
+  }
+  if (propval.str != NULL) {
+    retstring = strdup(propval.str);
+    free(propval.str);
+  }
+  return retstring;
+}
+
+
+/**
+ * Sets the synchronization partner of an MTP device. Note that
+ * we have no idea what the effect of setting this to "foobar"
+ * may be. But the general idea seems to be to tell which program
+ * shall synchronize with this device and tell others to leave
+ * it alone.
+ * @param device a pointer to the device to set the sync partner for.
+ * @param syncpartner the new synchronization partner for the device.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Get_Syncpartner()
+ */
+int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t *device,
+			 char const * const syncpartner)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+
+  if (!ptp_property_issupported(params, PTP_DPC_MTP_SynchronizationPartner)) {
+    return -1;
+  }
+  propval.str = (char *) syncpartner;
+  ret = ptp_setdevicepropvalue(params,
+			       PTP_DPC_MTP_SynchronizationPartner,
+			       &propval,
+			       PTP_DTC_STR);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "Error setting syncpartner.");
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * Checks if the device can stora a file of this size or
+ * if it's too big.
+ * @param device a pointer to the device.
+ * @param filesize the size of the file to check whether it will fit.
+ * @param storageid the ID of the storage to try to fit the file on.
+ * @return 0 if the file fits, any other value means failure.
+ */
+static int check_if_file_fits(LIBMTP_mtpdevice_t *device,
+			      LIBMTP_devicestorage_t *storage,
+			      uint64_t const filesize) {
+  PTPParams *params = (PTPParams *) device->params;
+  uint64_t freebytes;
+  int ret;
+
+  // If we cannot check the storage, no big deal.
+  if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
+    return 0;
+  }
+  
+  ret = get_storage_freespace(device, storage, &freebytes);
+  if (ret != 0) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "check_if_file_fits(): error checking free storage.");
+    return -1;
+  } else {
+    // See if it fits.
+    if (filesize > freebytes) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+
+/**
+ * This function retrieves the current battery level on the device.
+ * @param device a pointer to the device to get the battery level for.
+ * @param maximum_level a pointer to a variable that will hold the
+ *        maximum level of the battery if the call was successful.
+ * @param current_level a pointer to a variable that will hold the
+ *        current level of the battery if the call was successful.
+ *        A value of 0 means that the device is on external power.
+ * @return 0 if the storage info was successfully retrieved, any other
+ *        means failure. A typical cause of failure is that
+ *        the device does not support the battery level property.
+ */
+int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *device,
+			    uint8_t * const maximum_level,
+			    uint8_t * const current_level)
+{
+  PTPPropertyValue propval;
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+
+  *maximum_level = 0;
+  *current_level = 0;
+
+  if (FLAG_BROKEN_BATTERY_LEVEL(ptp_usb) ||
+      !ptp_property_issupported(params, PTP_DPC_BatteryLevel)) {
+    return -1;
+  }
+
+  ret = ptp_getdevicepropvalue(params, PTP_DPC_BatteryLevel, &propval, PTP_DTC_UINT8);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Batterylevel(): could not get device property value.");
+    return -1;
+  }
+
+  *maximum_level = device->maximum_battery_level;
+  *current_level = propval.u8;
+
+  return 0;
+}
+
+
+/**
+ * Formats device storage (if the device supports the operation).
+ * WARNING: This WILL delete all data from the device. Make sure you've
+ * got confirmation from the user BEFORE you call this function.
+ *
+ * @param device a pointer to the device containing the storage to format.
+ * @param storage the actual storage to format.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *device, LIBMTP_devicestorage_t *storage)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+
+  if (!ptp_operation_issupported(params,PTP_OC_FormatStore)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Format_Storage(): device does not support formatting storage.");
+    return -1;
+  }
+  ret = ptp_formatstore(params, storage->id);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Format_Storage(): failed to format storage.");
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * Helper function to extract a unicode property off a device.
+ * This is the standard way of retrieveing unicode device
+ * properties as described by the PTP spec.
+ * @param device a pointer to the device to get the property from.
+ * @param unicstring a pointer to a pointer that will hold the
+ *        property after this call is completed.
+ * @param property the property to retrieve.
+ * @return 0 on success, any other value means failure.
+ */
+static int get_device_unicode_property(LIBMTP_mtpdevice_t *device,
+				       char **unicstring, uint16_t property)
+{
+  PTPPropertyValue propval;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t *tmp;
+  uint16_t ret;
+  int i;
+
+  if (!ptp_property_issupported(params, property)) {
+    return -1;
+  }
+
+  // Unicode strings are 16bit unsigned integer arrays.
+  ret = ptp_getdevicepropvalue(params,
+			       property,
+			       &propval,
+			       PTP_DTC_AUINT16);
+  if (ret != PTP_RC_OK) {
+    // TODO: add a note on WHICH property that we failed to get.
+    *unicstring = NULL;
+    add_ptp_error_to_errorstack(device, ret, "get_device_unicode_property(): failed to get unicode property.");
+    return -1;
+  }
+
+  // Extract the actual array.
+  // printf("Array of %d elements\n", propval.a.count);
+  tmp = malloc((propval.a.count + 1)*sizeof(uint16_t));
+  for (i = 0; i < propval.a.count; i++) {
+    tmp[i] = propval.a.v[i].u16;
+    // printf("%04x ", tmp[i]);
+  }
+  tmp[propval.a.count] = 0x0000U;
+  free(propval.a.v);
+
+  *unicstring = utf16_to_utf8(device, tmp);
+
+  free(tmp);
+
+  return 0;
+}
+
+/**
+ * This function returns the secure time as an XML document string from
+ * the device.
+ * @param device a pointer to the device to get the secure time for.
+ * @param sectime the secure time string as an XML document or NULL if the call
+ *         failed or the secure time property is not supported. This string
+ *         must be <code>free()</code>:ed by the caller after use.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *device, char ** const sectime)
+{
+  return get_device_unicode_property(device, sectime, PTP_DPC_MTP_SecureTime);
+}
+
+/**
+ * This function returns the device (public key) certificate as an
+ * XML document string from the device.
+ * @param device a pointer to the device to get the device certificate for.
+ * @param devcert the device certificate as an XML string or NULL if the call
+ *        failed or the device certificate property is not supported. This
+ *        string must be <code>free()</code>:ed by the caller after use.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *device, char ** const devcert)
+{
+  return get_device_unicode_property(device, devcert, PTP_DPC_MTP_DeviceCertificate);
+}
+
+/**
+ * This function retrieves a list of supported file types, i.e. the file
+ * types that this device claims it supports, e.g. audio file types that
+ * the device can play etc. This list is mitigated to
+ * inlcude the file types that libmtp can handle, i.e. it will not list
+ * filetypes that libmtp will handle internally like playlists and folders.
+ * @param device a pointer to the device to get the filetype capabilities for.
+ * @param filetypes a pointer to a pointer that will hold the list of
+ *        supported filetypes if the call was successful. This list must
+ *        be <code>free()</code>:ed by the caller after use.
+ * @param length a pointer to a variable that will hold the length of the
+ *        list of supported filetypes if the call was successful.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Get_Filetype_Description()
+ */
+int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const filetypes,
+				  uint16_t * const length)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint16_t *localtypes;
+  uint16_t localtypelen;
+  uint32_t i;
+
+  // This is more memory than needed if there are unknown types, but what the heck.
+  localtypes = (uint16_t *) malloc(params->deviceinfo.ImageFormats_len * sizeof(uint16_t));
+  localtypelen = 0;
+
+  for (i=0;i<params->deviceinfo.ImageFormats_len;i++) {
+    uint16_t localtype = map_ptp_type_to_libmtp_type(params->deviceinfo.ImageFormats[i]);
+    if (localtype != LIBMTP_FILETYPE_UNKNOWN) {
+      localtypes[localtypelen] = localtype;
+      localtypelen++;
+    }
+  }
+  // The forgotten Ogg support on YP-10 and others...
+  if (FLAG_OGG_IS_UNKNOWN(ptp_usb)) {
+    localtypes = (uint16_t *) realloc(localtypes, (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
+    localtypes[localtypelen] = LIBMTP_FILETYPE_OGG;
+    localtypelen++;
+  }
+  // The forgotten FLAC support on Cowon iAudio S9 and others...
+  if (FLAG_FLAC_IS_UNKNOWN(ptp_usb)) {
+    localtypes = (uint16_t *) realloc(localtypes, (params->deviceinfo.ImageFormats_len+1) * sizeof(uint16_t));
+    localtypes[localtypelen] = LIBMTP_FILETYPE_FLAC;
+    localtypelen++;
+  }
+
+  *filetypes = localtypes;
+  *length = localtypelen;
+
+  return 0;
+}
+
+/**
+ * This function updates all the storage id's of a device and their
+ * properties, then creates a linked list and puts the list head into 
+ * the device struct. It also optionally sorts this list. If you want
+ * to display storage information in your application you should call
+ * this function, then dereference the device struct  
+ * (<code>device-&gt;storage</code>) to get out information on the storage.
+ *
+ * You need to call this everytime you want to update the
+ * <code>device-&gt;storage</code> list, for example anytime you need
+ * to check available storage somewhere.
+ *
+ * <b>WARNING:</b> since this list is dynamically updated, do not
+ * reference its fields in external applications by pointer! E.g
+ * do not put a reference to any <code>char *</code> field. instead
+ * <code>strncpy()</code> it!
+ *
+ * @param device a pointer to the device to get the storage for.
+ * @param sortby an integer that determines the sorting of the storage list. 
+ *        Valid sort methods are defined in libmtp.h with beginning with
+ *        LIBMTP_STORAGE_SORTBY_. 0 or LIBMTP_STORAGE_SORTBY_NOTSORTED to not 
+ *        sort.
+ * @return 0 on success, 1 success but only with storage id's, storage 
+ *        properities could not be retrieved and -1 means failure.
+ */
+int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *device, int const sortby)
+{
+  uint32_t i = 0;
+  PTPStorageInfo storageInfo;
+  PTPParams *params = (PTPParams *) device->params;
+  PTPStorageIDs storageIDs;
+  LIBMTP_devicestorage_t *storage = NULL;
+  LIBMTP_devicestorage_t *storageprev = NULL;
+
+  if (device->storage != NULL)
+    free_storage_list(device);
+
+  // if (!ptp_operation_issupported(params,PTP_OC_GetStorageIDs)) 
+  //   return -1;
+  if (ptp_getstorageids (params, &storageIDs) != PTP_RC_OK) 
+    return -1;
+  if (storageIDs.n < 1) 
+    return -1;
+
+  if (!ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) {
+    for (i = 0; i < storageIDs.n; i++) {
+
+      storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
+      storage->prev = storageprev;
+      if (storageprev != NULL)
+        storageprev->next = storage;
+      if (device->storage == NULL) 
+        device->storage = storage;
+
+      storage->id = storageIDs.Storage[i];
+      storage->StorageType = PTP_ST_Undefined;
+      storage->FilesystemType = PTP_FST_Undefined;
+      storage->AccessCapability = PTP_AC_ReadWrite;
+      storage->MaxCapacity = (uint64_t) -1;
+      storage->FreeSpaceInBytes = (uint64_t) -1;
+      storage->FreeSpaceInObjects = (uint64_t) -1;
+      storage->StorageDescription = strdup("Unknown storage");
+      storage->VolumeIdentifier = strdup("Unknown volume");
+      storage->next = NULL;
+
+      storageprev = storage;
+    }
+    free(storageIDs.Storage);
+    return 1;
+  } else {
+    for (i = 0; i < storageIDs.n; i++) {
+      uint16_t ret;
+      ret = ptp_getstorageinfo(params, storageIDs.Storage[i], &storageInfo);
+      if (ret != PTP_RC_OK) {
+	add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Storage(): Could not get storage info.");
+	if (device->storage != NULL) {
+          free_storage_list(device);
+	}
+	return -1;
+      }
+
+      storage = (LIBMTP_devicestorage_t *) malloc(sizeof(LIBMTP_devicestorage_t));
+      storage->prev = storageprev;
+      if (storageprev != NULL)
+        storageprev->next = storage;
+      if (device->storage == NULL)
+        device->storage = storage;
+
+      storage->id = storageIDs.Storage[i];
+      storage->StorageType = storageInfo.StorageType;
+      storage->FilesystemType = storageInfo.FilesystemType;
+      storage->AccessCapability = storageInfo.AccessCapability;
+      storage->MaxCapacity = storageInfo.MaxCapability;
+      storage->FreeSpaceInBytes = storageInfo.FreeSpaceInBytes;
+      storage->FreeSpaceInObjects = storageInfo.FreeSpaceInImages;
+      storage->StorageDescription = storageInfo.StorageDescription;
+      storage->VolumeIdentifier = storageInfo.VolumeLabel;
+      storage->next = NULL;
+
+      storageprev = storage;
+    }
+
+    if (storage != NULL)
+      storage->next = NULL;
+
+    sort_storage_by(device,sortby);
+    free(storageIDs.Storage);
+    return 0;
+  }
+}
+
+/**
+ * This creates a new file metadata structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_destroy_file_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings, e.g.:
+ *
+ * <pre>
+ * LIBMTP_file_t *file = LIBMTP_new_file_t();
+ * file->filename = strdup(namestr);
+ * ....
+ * LIBMTP_destroy_file_t(file);
+ * </pre>
+ *
+ * @return a pointer to the newly allocated metadata structure.
+ * @see LIBMTP_destroy_file_t()
+ */
+LIBMTP_file_t *LIBMTP_new_file_t(void)
+{
+  LIBMTP_file_t *new = (LIBMTP_file_t *) malloc(sizeof(LIBMTP_file_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->filename = NULL;
+  new->item_id = 0;
+  new->parent_id = 0;
+  new->storage_id = 0;
+  new->filesize = 0;
+  new->modificationdate = 0;
+  new->filetype = LIBMTP_FILETYPE_UNKNOWN;
+  new->next = NULL;
+  return new;
+}
+
+/**
+ * This destroys a file metadata structure and deallocates the memory
+ * used by it, including any strings. Never use a file metadata
+ * structure again after calling this function on it.
+ * @param file the file metadata to destroy.
+ * @see LIBMTP_new_file_t()
+ */
+void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
+{
+  if (file == NULL) {
+    return;
+  }
+  if (file->filename != NULL)
+    free(file->filename);
+  free(file);
+  return;
+}
+
+/**
+* THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
+ * NOT TO USE IT.
+ * @see LIBMTP_Get_Filelisting_With_Callback()
+ */
+LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *device)
+{
+  printf("WARNING: LIBMTP_Get_Filelisting() is deprecated.\n");
+  printf("WARNING: please update your code to use LIBMTP_Get_Filelisting_With_Callback()\n");
+  return LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
+}
+
+/**
+ * This returns a long list of all files available
+ * on the current MTP device. Folders will not be returned, but abstract
+ * entities like playlists and albums will show up as "files". Typical usage:
+ *
+ * <pre>
+ * LIBMTP_file_t *filelist;
+ *
+ * filelist = LIBMTP_Get_Filelisting_With_Callback(device, callback, data);
+ * while (filelist != NULL) {
+ *   LIBMTP_file_t *tmp;
+ *
+ *   // Do something on each element in the list here...
+ *   tmp = filelist;
+ *   filelist = filelist->next;
+ *   LIBMTP_destroy_file_t(tmp);
+ * }
+ * </pre>
+ *
+ * If you want to group your file listing by storage (per storage unit) or
+ * arrange files into folders, you must dereference the <code>storage_id</code>
+ * and/or <code>parent_id</code> field of the returned <code>LIBMTP_file_t</code>
+ * struct. To arrange by folders or files you typically have to create the proper
+ * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or 
+ * <code>LIBMTP_Get_Folder_List()</code> first.
+ *
+ * @param device a pointer to the device to get the file listing for.
+ * @param callback a function to be called during the tracklisting retrieveal
+ *        for displaying progress bars etc, or NULL if you don't want
+ *        any callbacks.
+ * @param data a user-defined pointer that is passed along to
+ *        the <code>progress</code> function in order to
+ *        pass along some user defined data to the progress
+ *        updates. If not used, set this to NULL.
+ * @return a list of files that can be followed using the <code>next</code>
+ *        field of the <code>LIBMTP_file_t</code> data structure.
+ *        Each of the metadata tags must be freed after use, and may
+ *        contain only partial metadata information, i.e. one or several
+ *        fields may be NULL or 0.
+ * @see LIBMTP_Get_Filemetadata()
+ */
+LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
+                                                    LIBMTP_progressfunc_t const callback,
+                                                    void const * const data)
+{
+  uint32_t i = 0;
+  LIBMTP_file_t *retfiles = NULL;
+  LIBMTP_file_t *curfile = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint16_t ret;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  for (i = 0; i < params->nrofobjects; i++) {
+    LIBMTP_file_t *file;
+    PTPObject *ob, *xob;
+
+    if (callback != NULL)
+      callback(i, params->nrofobjects, data);
+
+    ob = &params->objects[i];
+
+    if (ob->oi.ObjectFormat == PTP_OFC_Association) {
+      // MTP use this object format for folders which means
+      // these "files" will turn up on a folder listing instead.
+      continue;
+    }
+
+    // Allocate a new file type
+    file = LIBMTP_new_file_t();
+
+    file->parent_id = ob->oi.ParentObject;
+    file->storage_id = ob->oi.StorageID;
+
+    // This is some sort of unique ID so we can keep track of the track.
+    file->item_id = ob->oid;
+
+    // Set the filetype
+    file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+    
+    // Set the modification date
+    file->modificationdate = ob->oi.ModificationDate;
+
+    // Original file-specific properties
+    // We only have 32-bit file size here; if we find it, we use the 
+    // PTP_OPC_ObjectSize property which has 64bit precision.
+    file->filesize = ob->oi.ObjectCompressedSize;
+    if (ob->oi.Filename != NULL) {
+      file->filename = strdup(ob->oi.Filename);
+    }
+
+    /*
+     * A special quirk for devices that doesn't quite
+     * remember that some files marked as "unknown" type are
+     * actually OGG or FLAC files. We look at the filename extension
+     * and see if it happens that this was atleast named "ogg" or "flac"
+     * and fall back on this heuristic approach in that case, 
+     * for these bugged devices only.
+     */
+    if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
+      if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
+	   FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+	  has_ogg_extension(file->filename))
+	file->filetype = LIBMTP_FILETYPE_OGG;
+      if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
+	  has_flac_extension(file->filename))
+	file->filetype = LIBMTP_FILETYPE_FLAC;
+    }
+
+    /*
+     * If we have a cached, large set of metadata, then use it!
+     */
+    ret = ptp_object_want (params, ob->oid, PTPOBJECT_MTPPROPLIST_LOADED, &xob);
+    if (ob->mtpprops) {
+      MTPProperties *prop = ob->mtpprops;
+      int i;
+
+      for (i=0;i<ob->nrofmtpprops;i++) {
+	// Pick ObjectSize here...
+	if (prop->property == PTP_OPC_ObjectSize) {
+	  if (device->object_bitsize == 64) {
+	    file->filesize = prop->propval.u64;
+	  } else {
+	    file->filesize = prop->propval.u32;
+	  }
+	  break;
+	}
+	prop++;
+      }
+    } else {
+      uint16_t *props = NULL;
+      uint32_t propcnt = 0;
+      
+      // First see which properties can be retrieved for this object format
+      ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
+      if (ret != PTP_RC_OK) {
+	add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filelisting_With_Callback(): call to ptp_mtp_getobjectpropssupported() failed.");
+	// Silently fall through.
+      } else {
+        int i;
+	for (i=0;i<propcnt;i++) {
+	  switch (props[i]) {
+	  case PTP_OPC_ObjectSize:
+	    if (device->object_bitsize == 64) {
+	      file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+	    } else {
+	      file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+	    }
+	    break;
+	  default:
+	    break;
+	  }
+	}
+	free(props);
+      }
+    }
+    
+    // Add track to a list that will be returned afterwards.
+    if (retfiles == NULL) {
+      retfiles = file;
+      curfile = file;
+    } else {
+      curfile->next = file;
+      curfile = file;
+    }
+
+    // Call listing callback
+    // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
+    
+  } // Handle counting loop
+  return retfiles;
+}
+
+/**
+ * This function retrieves the metadata for a single file off
+ * the device.
+ *
+ * Do not call this function repeatedly! The file handles are linearly
+ * searched O(n) and the call may involve (slow) USB traffic, so use
+ * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
+ * as an efficient data structure such as a hash list.
+ *
+ * Incidentally this function will return metadata for
+ * a folder (association) as well, but this is not a proper use
+ * of it, it is intended for file manipulation, not folder manipulation.
+ *
+ * @param device a pointer to the device to get the file metadata from.
+ * @param fileid the object ID of the file that you want the metadata for.
+ * @return a metadata entry on success or NULL on failure.
+ * @see LIBMTP_Get_Filelisting()
+ */
+LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
+{
+  uint32_t i = 0;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  PTPObject *ob;
+  LIBMTP_file_t *file;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  ret = ptp_object_want (params, fileid, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
+  if (ret != PTP_RC_OK)
+    return NULL;
+
+  // Allocate a new file type
+  file = LIBMTP_new_file_t();
+  
+  file->parent_id = ob->oi.ParentObject;
+  file->storage_id = ob->oi.StorageID;
+
+  // Set the filetype
+  file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+
+  // Original file-specific properties
+  
+  // We only have 32-bit file size here; later we use the PTP_OPC_ObjectSize property
+  file->filesize = ob->oi.ObjectCompressedSize;
+  if (ob->oi.Filename != NULL) {
+    file->filename = strdup(ob->oi.Filename);
+  }
+
+  // This is some sort of unique ID so we can keep track of the file.
+  file->item_id = fileid;
+
+  /*
+   * If we have a cached, large set of metadata, then use it!
+   */
+  if (ob->mtpprops) {
+    MTPProperties *prop = ob->mtpprops;
+    
+    for (i=0;i<ob->nrofmtpprops;i++,prop++) {
+      // Pick ObjectSize here...
+      if (prop->property == PTP_OPC_ObjectSize) {
+	// This may already be set, but this 64bit precision value 
+	// is better than the PTP 32bit value, so let it override.
+	if (device->object_bitsize == 64) {
+	  file->filesize = prop->propval.u64;
+	} else {
+	  file->filesize = prop->propval.u32;
+	}
+	break;
+      }
+    }
+  } else {
+    uint16_t *props = NULL;
+    uint32_t propcnt = 0;
+    
+    // First see which properties can be retrieved for this object format
+    ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(file->filetype), &propcnt, &props);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filemetadata(): call to ptp_mtp_getobjectpropssupported() failed.");
+      // Silently fall through.
+    } else {
+      for (i=0;i<propcnt;i++) {
+	switch (props[i]) {
+	case PTP_OPC_ObjectSize:
+	  if (device->object_bitsize == 64) {
+	    file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+	  } else {
+	    file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0);
+	  }
+	  break;
+	default:
+	  break;
+	}
+      }
+      free(props);
+    }
+  }
+  
+  return file;
+}
+
+/**
+ * This creates a new track metadata structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_destroy_track_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings, e.g.:
+ *
+ * <pre>
+ * LIBMTP_track_t *track = LIBMTP_new_track_t();
+ * track->title = strdup(titlestr);
+ * ....
+ * LIBMTP_destroy_track_t(track);
+ * </pre>
+ *
+ * @return a pointer to the newly allocated metadata structure.
+ * @see LIBMTP_destroy_track_t()
+ */
+LIBMTP_track_t *LIBMTP_new_track_t(void)
+{
+  LIBMTP_track_t *new = (LIBMTP_track_t *) malloc(sizeof(LIBMTP_track_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->item_id = 0;
+  new->parent_id = 0;
+  new->storage_id = 0;
+  new->title = NULL;
+  new->artist = NULL;
+  new->composer = NULL;
+  new->album = NULL;
+  new->genre = NULL;
+  new->date = NULL;
+  new->filename = NULL;
+  new->duration = 0;
+  new->tracknumber = 0;
+  new->filesize = 0;
+  new->filetype = LIBMTP_FILETYPE_UNKNOWN;
+  new->samplerate = 0;
+  new->nochannels = 0;
+  new->wavecodec = 0;
+  new->bitrate = 0;
+  new->bitratetype = 0;
+  new->rating = 0;
+  new->usecount = 0;
+  new->modificationdate = 0;
+  new->next = NULL;
+  return new;
+}
+
+/**
+ * This destroys a track metadata structure and deallocates the memory
+ * used by it, including any strings. Never use a track metadata
+ * structure again after calling this function on it.
+ * @param track the track metadata to destroy.
+ * @see LIBMTP_new_track_t()
+ */
+void LIBMTP_destroy_track_t(LIBMTP_track_t *track)
+{
+  if (track == NULL) {
+    return;
+  }
+  if (track->title != NULL)
+    free(track->title);
+  if (track->artist != NULL)
+    free(track->artist);
+  if (track->composer != NULL)
+    free(track->composer);
+  if (track->album != NULL)
+    free(track->album);
+  if (track->genre != NULL)
+    free(track->genre);
+  if (track->date != NULL)
+    free(track->date);
+  if (track->filename != NULL)
+    free(track->filename);
+  free(track);
+  return;
+}
+
+/**
+ * This function maps and copies a property onto the track metadata if applicable.
+ */
+static void pick_property_to_track_metadata(LIBMTP_mtpdevice_t *device, MTPProperties *prop, LIBMTP_track_t *track)
+{
+  switch (prop->property) {
+  case PTP_OPC_Name:
+    if (prop->propval.str != NULL)
+      track->title = strdup(prop->propval.str);
+    else
+      track->title = NULL;
+    break;
+  case PTP_OPC_Artist:
+    if (prop->propval.str != NULL)
+      track->artist = strdup(prop->propval.str);
+    else
+      track->artist = NULL;
+    break;
+  case PTP_OPC_Composer:
+    if (prop->propval.str != NULL)
+      track->composer = strdup(prop->propval.str);
+    else
+      track->composer = NULL;
+    break;
+  case PTP_OPC_Duration:
+    track->duration = prop->propval.u32;
+    break;
+  case PTP_OPC_Track:
+    track->tracknumber = prop->propval.u16;
+    break;
+  case PTP_OPC_Genre:
+    if (prop->propval.str != NULL)
+      track->genre = strdup(prop->propval.str);
+    else
+      track->genre = NULL;
+    break;
+  case PTP_OPC_AlbumName:
+    if (prop->propval.str != NULL)
+      track->album = strdup(prop->propval.str);
+    else
+      track->album = NULL;
+    break;
+  case PTP_OPC_OriginalReleaseDate:
+    if (prop->propval.str != NULL)
+      track->date = strdup(prop->propval.str);
+    else
+      track->date = NULL;
+    break;
+    // These are, well not so important.
+  case PTP_OPC_SampleRate:
+    track->samplerate = prop->propval.u32;
+    break;
+  case PTP_OPC_NumberOfChannels:
+    track->nochannels = prop->propval.u16;
+    break;
+  case PTP_OPC_AudioWAVECodec:
+    track->wavecodec = prop->propval.u32;
+    break;
+  case PTP_OPC_AudioBitRate:
+    track->bitrate = prop->propval.u32;
+    break;
+  case PTP_OPC_BitRateType:
+    track->bitratetype = prop->propval.u16;
+    break;
+  case PTP_OPC_Rating:
+    track->rating = prop->propval.u16;
+    break;
+  case PTP_OPC_UseCount:
+    track->usecount = prop->propval.u32;
+    break;
+  case PTP_OPC_ObjectSize:
+    if (device->object_bitsize == 64) {
+      track->filesize = prop->propval.u64;
+    } else {
+      track->filesize = prop->propval.u32;
+    }
+    break;
+  default:
+    break;
+  }
+}
+
+/**
+ * This function retrieves the track metadata for a track
+ * given by a unique ID.
+ * @param device a pointer to the device to get the track metadata off.
+ * @param trackid the unique ID of the track.
+ * @param objectformat the object format of this track, so we know what it supports.
+ * @param track a metadata set to fill in.
+ */
+static void get_track_metadata(LIBMTP_mtpdevice_t *device, uint16_t objectformat,
+			       LIBMTP_track_t *track)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  uint32_t i;
+  MTPProperties *prop;
+  PTPObject *ob;
+
+  /*
+   * If we have a cached, large set of metadata, then use it!
+   */
+  ret = ptp_object_want(params, track->item_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
+  if (ob->mtpprops) {
+    prop = ob->mtpprops;
+    for (i=0;i<ob->nrofmtpprops;i++,prop++)
+      pick_property_to_track_metadata(device, prop, track);
+  } else {
+    uint16_t *props = NULL;
+    uint32_t propcnt = 0;
+
+    // First see which properties can be retrieved for this object format
+    ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(track->filetype), &propcnt, &props);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
+      // Just bail out for now, nothing is ever set.
+      return;
+    } else {
+      for (i=0;i<propcnt;i++) {
+	switch (props[i]) {
+	case PTP_OPC_Name:
+	  track->title = get_string_from_object(device, track->item_id, PTP_OPC_Name);
+	  break;
+	case PTP_OPC_Artist:
+	  track->artist = get_string_from_object(device, track->item_id, PTP_OPC_Artist);
+	  break;
+	case PTP_OPC_Composer:
+	  track->composer = get_string_from_object(device, track->item_id, PTP_OPC_Composer);
+	  break;
+	case PTP_OPC_Duration:
+	  track->duration = get_u32_from_object(device, track->item_id, PTP_OPC_Duration, 0);
+	  break;
+	case PTP_OPC_Track:
+	  track->tracknumber = get_u16_from_object(device, track->item_id, PTP_OPC_Track, 0);
+	  break;
+	case PTP_OPC_Genre:
+	  track->genre = get_string_from_object(device, track->item_id, PTP_OPC_Genre);
+	  break;
+	case PTP_OPC_AlbumName:
+	  track->album = get_string_from_object(device, track->item_id, PTP_OPC_AlbumName);
+	  break;
+	case PTP_OPC_OriginalReleaseDate:
+	  track->date = get_string_from_object(device, track->item_id, PTP_OPC_OriginalReleaseDate);
+	  break;
+	  // These are, well not so important.
+	case PTP_OPC_SampleRate:
+	  track->samplerate = get_u32_from_object(device, track->item_id, PTP_OPC_SampleRate, 0);
+	  break;
+	case PTP_OPC_NumberOfChannels:
+	  track->nochannels = get_u16_from_object(device, track->item_id, PTP_OPC_NumberOfChannels, 0);
+	  break;
+	case PTP_OPC_AudioWAVECodec:
+	  track->wavecodec = get_u32_from_object(device, track->item_id, PTP_OPC_AudioWAVECodec, 0);
+	  break;
+	case PTP_OPC_AudioBitRate:
+	  track->bitrate = get_u32_from_object(device, track->item_id, PTP_OPC_AudioBitRate, 0);
+	  break;
+	case PTP_OPC_BitRateType:
+	  track->bitratetype = get_u16_from_object(device, track->item_id, PTP_OPC_BitRateType, 0);
+	  break;
+	case PTP_OPC_Rating:
+	  track->rating = get_u16_from_object(device, track->item_id, PTP_OPC_Rating, 0);
+	  break;
+	case PTP_OPC_UseCount:
+	  track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0);
+	  break;
+	case PTP_OPC_ObjectSize:
+	  if (device->object_bitsize == 64) {
+	    track->filesize = get_u64_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
+	  } else {
+	    track->filesize = (uint64_t) get_u32_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0);
+	  }
+	  break;
+	}
+      }
+      free(props);
+    }
+  }
+}
+
+/**
+ * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
+ * NOT TO USE IT.
+ * @see LIBMTP_Get_Tracklisting_With_Callback()
+ */
+LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t *device)
+{
+  printf("WARNING: LIBMTP_Get_Tracklisting() is deprecated.\n");
+  printf("WARNING: please update your code to use LIBMTP_Get_Tracklisting_With_Callback()\n");
+  return LIBMTP_Get_Tracklisting_With_Callback(device, NULL, NULL);
+}
+
+/**
+ * This returns a long list of all tracks available on the current MTP device.
+ * Tracks include multimedia objects, both music tracks and video tracks.
+ * Typical usage:
+ *
+ * <pre>
+ * LIBMTP_track_t *tracklist;
+ *
+ * tracklist = LIBMTP_Get_Tracklisting_With_Callback(device, callback, data);
+ * while (tracklist != NULL) {
+ *   LIBMTP_track_t *tmp;
+ *
+ *   // Do something on each element in the list here...
+ *   tmp = tracklist;
+ *   tracklist = tracklist->next;
+ *   LIBMTP_destroy_track_t(tmp);
+ * }
+ * </pre>
+ *
+ * If you want to group your track listing by storage (per storage unit) or
+ * arrange tracks into folders, you must dereference the <code>storage_id</code>
+ * and/or <code>parent_id</code> field of the returned <code>LIBMTP_track_t</code>
+ * struct. To arrange by folders or files you typically have to create the proper
+ * trees by calls to <code>LIBMTP_Get_Storage()</code> and/or 
+ * <code>LIBMTP_Get_Folder_List()</code> first.
+ *
+ * @param device a pointer to the device to get the track listing for.
+ * @param callback a function to be called during the tracklisting retrieveal
+ *        for displaying progress bars etc, or NULL if you don't want
+ *        any callbacks.
+ * @param data a user-defined pointer that is passed along to
+ *        the <code>progress</code> function in order to
+ *        pass along some user defined data to the progress
+ *        updates. If not used, set this to NULL.
+ * @return a list of tracks that can be followed using the <code>next</code>
+ *        field of the <code>LIBMTP_track_t</code> data structure.
+ *        Each of the metadata tags must be freed after use, and may
+ *        contain only partial metadata information, i.e. one or several
+ *        fields may be NULL or 0.
+ * @see LIBMTP_Get_Trackmetadata()
+ */
+LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t *device,
+                                                      LIBMTP_progressfunc_t const callback,
+                                                      void const * const data)
+{
+  uint32_t i = 0;
+  LIBMTP_track_t *retracks = NULL;
+  LIBMTP_track_t *curtrack = NULL;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  for (i = 0; i < params->nrofobjects; i++) {
+    LIBMTP_track_t *track;
+    PTPObject *ob;
+    LIBMTP_filetype_t mtptype;
+
+    if (callback != NULL)
+      callback(i, params->nrofobjects, data);
+
+    ob = &params->objects[i];
+    mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+
+    // Ignore stuff we don't know how to handle...
+    // TODO: get this list as an intersection of the sets
+    // supported by the device and the from the device and
+    // all known track files?
+    if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
+	// This row lets through undefined files for examination since they may be forgotten OGG files.
+	(ob->oi.ObjectFormat != PTP_OFC_Undefined || 
+	 (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
+	  !FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
+	  !FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
+	) {
+      //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
+      continue;
+    }
+
+    // Allocate a new track type
+    track = LIBMTP_new_track_t();
+      
+    // This is some sort of unique ID so we can keep track of the track.
+    track->item_id = ob->oid;
+    track->parent_id = ob->oi.ParentObject;
+    track->storage_id = ob->oi.StorageID;
+    track->modificationdate = ob->oi.ModificationDate;
+
+    track->filetype = mtptype;
+
+    // Original file-specific properties
+    track->filesize = ob->oi.ObjectCompressedSize;
+    if (ob->oi.Filename != NULL) {
+      track->filename = strdup(ob->oi.Filename);
+    }
+
+    get_track_metadata(device, ob->oi.ObjectFormat, track);
+
+    /*
+     * A special quirk for iriver devices that doesn't quite
+     * remember that some files marked as "unknown" type are
+     * actually OGG or FLAC files. We look at the filename extension
+     * and see if it happens that this was atleast named "ogg" or "flac"
+     * and fall back on this heuristic approach in that case,
+     * for these bugged devices only.
+     */
+    if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
+	track->filename != NULL) {
+      if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
+	   FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+	  has_ogg_extension(track->filename))
+	track->filetype = LIBMTP_FILETYPE_OGG;
+      else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
+	       has_flac_extension(track->filename))
+	track->filetype = LIBMTP_FILETYPE_FLAC;
+      else {
+	// This was not an OGG/FLAC file so discard it and continue
+	LIBMTP_destroy_track_t(track);
+	continue;
+      }
+    }
+    
+    // Add track to a list that will be returned afterwards.
+    if (retracks == NULL) {
+      retracks = track;
+      curtrack = track;
+    } else {
+      curtrack->next = track;
+      curtrack = track;
+    }
+    
+    // Call listing callback
+    // double progressPercent = (double)i*(double)100.0 / (double)params->handles.n;
+    
+    
+  } // Handle counting loop
+  return retracks;
+}
+
+/**
+ * This function retrieves the metadata for a single track off
+ * the device.
+ *
+ * Do not call this function repeatedly! The track handles are linearly
+ * searched O(n) and the call may involve (slow) USB traffic, so use
+ * <code>LIBMTP_Get_Tracklisting()</code> and cache the tracks, preferably
+ * as an efficient data structure such as a hash list.
+ *
+ * @param device a pointer to the device to get the track metadata from.
+ * @param trackid the object ID of the track that you want the metadata for.
+ * @return a track metadata entry on success or NULL on failure.
+ * @see LIBMTP_Get_Tracklisting()
+ */
+LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t *device, uint32_t const trackid)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  PTPObject *ob;
+  LIBMTP_track_t *track;
+  LIBMTP_filetype_t mtptype;
+  uint16_t ret;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0)
+    flush_handles(device);
+
+  ret = ptp_object_want (params, trackid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK)
+    return NULL;
+
+  mtptype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+
+  // Ignore stuff we don't know how to handle...
+  if (!LIBMTP_FILETYPE_IS_TRACK(mtptype) &&
+      /*
+       * This row lets through undefined files for examination
+       * since they may be forgotten OGG or FLAC files.
+       */
+      (ob->oi.ObjectFormat != PTP_OFC_Undefined || 
+       (!FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) &&
+	!FLAG_OGG_IS_UNKNOWN(ptp_usb) &&
+	!FLAG_FLAC_IS_UNKNOWN(ptp_usb)))
+      ) {
+    //printf("Not a music track (name: %s format: %d), skipping...\n", oi->Filename, oi->ObjectFormat);
+    return NULL;
+  }
+
+  // Allocate a new track type
+  track = LIBMTP_new_track_t();
+  
+  // This is some sort of unique ID so we can keep track of the track.
+  track->item_id = ob->oid;
+  track->parent_id = ob->oi.ParentObject;
+  track->storage_id = ob->oi.StorageID;
+  track->modificationdate = ob->oi.ModificationDate;
+
+  track->filetype = mtptype;
+
+  // Original file-specific properties
+  track->filesize = ob->oi.ObjectCompressedSize;
+  if (ob->oi.Filename != NULL) {
+    track->filename = strdup(ob->oi.Filename);
+  }
+
+  /*
+   * A special quirk for devices that doesn't quite
+   * remember that some files marked as "unknown" type are
+   * actually OGG or FLAC files. We look at the filename extension
+   * and see if it happens that this was atleast named "ogg"
+   * and fall back on this heuristic approach in that case, 
+   * for these bugged devices only.
+   */
+  if (track->filetype == LIBMTP_FILETYPE_UNKNOWN &&
+      track->filename != NULL) {
+    if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
+	 FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+	has_ogg_extension(track->filename))
+      track->filetype = LIBMTP_FILETYPE_OGG;
+    else if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
+	     has_flac_extension(track->filename))
+      track->filetype = LIBMTP_FILETYPE_FLAC;
+    else {
+      // This was not an OGG/FLAC file so discard it
+      LIBMTP_destroy_track_t(track);
+      return NULL;
+    }
+  }
+  get_track_metadata(device, ob->oi.ObjectFormat, track);
+  return track;
+}
+
+/**
+ * This is a manual conversion from MTPDataGetFunc to PTPDataGetFunc
+ * to isolate the internal type.
+ */
+static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen)
+{
+  MTPDataHandler *handler = (MTPDataHandler *)priv;
+  uint16_t ret;
+  uint32_t local_gotlen = 0;
+  ret = handler->getfunc(params, handler->priv, wantlen, data, &local_gotlen);
+  *gotlen = local_gotlen;
+  switch (ret)
+  {
+    case LIBMTP_HANDLER_RETURN_OK:
+      return PTP_RC_OK;
+    case LIBMTP_HANDLER_RETURN_ERROR:
+      return PTP_ERROR_IO;
+    case LIBMTP_HANDLER_RETURN_CANCEL:
+      return PTP_ERROR_CANCEL;
+    default:
+      return PTP_ERROR_IO;
+  }
+}
+
+/**
+ * This is a manual conversion from MTPDataPutFunc to PTPDataPutFunc
+ * to isolate the internal type.
+ */
+static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen)
+{
+  MTPDataHandler *handler = (MTPDataHandler *)priv;
+  uint16_t ret;
+  uint32_t local_putlen = 0;
+  ret = handler->putfunc(params, handler->priv, sendlen, data, &local_putlen);
+  *putlen = local_putlen;
+  switch (ret)
+  {
+    case LIBMTP_HANDLER_RETURN_OK:
+      return PTP_RC_OK;
+    case LIBMTP_HANDLER_RETURN_ERROR:
+      return PTP_ERROR_IO;
+    case LIBMTP_HANDLER_RETURN_CANCEL:
+      return PTP_ERROR_CANCEL;
+    default:
+      return PTP_ERROR_IO;
+  }
+}
+
+/**
+ * This gets a file off the device to a local file identified
+ * by a filename.
+ * @param device a pointer to the device to get the track from.
+ * @param id the file ID of the file to retrieve.
+ * @param path a filename to use for the retrieved file.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Get_File_To_File_Descriptor()
+ */
+int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
+			 char const * const path, LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  int fd = -1;
+  int ret;
+
+  // Sanity check
+  if (path == NULL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Bad arguments, path was NULL.");
+    return -1;
+  }
+
+  // Open file
+#ifdef __WIN32__
+#ifdef USE_WINDOWS_IO_H
+  if ( (fd = _open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,_S_IREAD)) == -1 ) {
+#else
+  if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IRWXU)) == -1 ) {
+#endif
+#else
+  if ( (fd = open(path, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP)) == -1) {
+#endif
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File(): Could not create file.");
+    return -1;
+  }
+
+  ret = LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
+
+  // Close file
+  close(fd);
+
+  // Delete partial file.
+  if (ret == -1) {
+    unlink(path);
+  }
+
+  return ret;
+}
+
+/**
+ * This gets a file off the device to a file identified
+ * by a file descriptor.
+ *
+ * This function can potentially be used for streaming
+ * files off the device for playback or broadcast for example,
+ * by downloading the file into a stream sink e.g. a socket.
+ *
+ * @param device a pointer to the device to get the file from.
+ * @param id the file ID of the file to retrieve.
+ * @param fd a local file descriptor to write the file to.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Get_File_To_File()
+ */
+int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
+					uint32_t const id,
+					int const fd,
+					LIBMTP_progressfunc_t const callback,
+					void const * const data)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  PTPObject *ob;
+
+  ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
+    return -1;
+  }
+  if (ob->oi.ObjectFormat == PTP_OFC_Association) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
+    return -1;
+  }
+
+  // Callbacks
+  ptp_usb->callback_active = 1;
+  ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
+    PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
+  ptp_usb->current_transfer_complete = 0;
+  ptp_usb->current_transfer_callback = callback;
+  ptp_usb->current_transfer_callback_data = data;
+
+  ret = ptp_getobject_tofd(params, id, fd);
+
+  ptp_usb->callback_active = 0;
+  ptp_usb->current_transfer_callback = NULL;
+  ptp_usb->current_transfer_callback_data = NULL;
+
+  if (ret == PTP_ERROR_CANCEL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
+    return -1;
+  }
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * This gets a file off the device and calls put_func
+ * with chunks of data
+ *
+ * @param device a pointer to the device to get the file from.
+ * @param id the file ID of the file to retrieve.
+ * @param put_func the function to call when we have data.
+ * @param priv the user-defined pointer that is passed to
+ *             <code>put_func</code>.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ */
+int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *device,
+					uint32_t const id,
+					MTPDataPutFunc put_func,
+          void * priv,
+					LIBMTP_progressfunc_t const callback,
+					void const * const data)
+{
+  PTPObject *ob;
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+
+  ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
+    return -1;
+  }
+  if (ob->oi.ObjectFormat == PTP_OFC_Association) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
+    return -1;
+  }
+
+  // Callbacks
+  ptp_usb->callback_active = 1;
+  ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
+    PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
+  ptp_usb->current_transfer_complete = 0;
+  ptp_usb->current_transfer_callback = callback;
+  ptp_usb->current_transfer_callback_data = data;
+
+  MTPDataHandler mtp_handler;
+  mtp_handler.getfunc = NULL;
+  mtp_handler.putfunc = put_func;
+  mtp_handler.priv = priv;
+
+  PTPDataHandler handler;
+  handler.getfunc = NULL;
+  handler.putfunc = put_func_wrapper;
+  handler.priv = &mtp_handler;
+
+  ret = ptp_getobject_to_handler(params, id, &handler);
+
+  ptp_usb->callback_active = 0;
+  ptp_usb->current_transfer_callback = NULL;
+  ptp_usb->current_transfer_callback_data = NULL;
+
+  if (ret == PTP_ERROR_CANCEL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Get_File_From_File_Descriptor(): Cancelled transfer.");
+    return -1;
+  }
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device.");
+    return -1;
+  }
+
+  return 0;
+}
+
+
+/**
+ * This gets a track off the device to a file identified
+ * by a filename. This is actually just a wrapper for the
+ * \c LIBMTP_Get_Track_To_File() function.
+ * @param device a pointer to the device to get the track from.
+ * @param id the track ID of the track to retrieve.
+ * @param path a filename to use for the retrieved track.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Get_Track_To_File_Descriptor()
+ */
+int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t *device, uint32_t const id,
+			 char const * const path, LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  // This is just a wrapper
+  return LIBMTP_Get_File_To_File(device, id, path, callback, data);
+}
+
+/**
+ * This gets a track off the device to a file identified
+ * by a file descriptor. This is actually just a wrapper for
+ * the \c LIBMTP_Get_File_To_File_Descriptor() function.
+ * @param device a pointer to the device to get the track from.
+ * @param id the track ID of the track to retrieve.
+ * @param fd a file descriptor to write the track to.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Get_Track_To_File()
+ */
+int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
+					uint32_t const id,
+					int const fd,
+					LIBMTP_progressfunc_t const callback,
+					void const * const data)
+{
+  // This is just a wrapper
+  return LIBMTP_Get_File_To_File_Descriptor(device, id, fd, callback, data);
+}
+
+/**
+ * This gets a track off the device to a handler function.
+ * This is actually just a wrapper for
+ * the \c LIBMTP_Get_File_To_Handler() function.
+ * @param device a pointer to the device to get the track from.
+ * @param id the track ID of the track to retrieve.
+ * @param put_func the function to call when we have data.
+ * @param priv the user-defined pointer that is passed to
+ *             <code>put_func</code>.
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ */
+int LIBMTP_Get_Track_To_Handler(LIBMTP_mtpdevice_t *device,
+					uint32_t const id,
+					MTPDataPutFunc put_func,
+          void * priv,
+					LIBMTP_progressfunc_t const callback,
+					void const * const data)
+{
+  // This is just a wrapper
+  return LIBMTP_Get_File_To_Handler(device, id, put_func, priv, callback, data);
+}
+
+/**
+ * This function sends a track from a local file to an
+ * MTP device. A filename and a set of metadata must be
+ * given as input.
+ * @param device a pointer to the device to send the track to.
+ * @param path the filename of a local file which will be sent.
+ * @param metadata a track metadata set to be written along with the file.
+ *        After this call the field <code>metadata-&gt;item_id</code>
+ *        will contain the new track ID. Other fields such
+ *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
+ *        or <code>metadata-&gt;storage_id</code> may also change during this 
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>metadata-&gt;parent_id</code> should be set to the parent 
+ *        (e.g. folder) to store this track in. Since some 
+ *        devices are a bit picky about where files
+ *        are placed, a default folder will be chosen if libmtp
+ *        has detected one for the current filetype and this
+ *        parameter is set to 0. If this is 0 and no default folder
+ *        can be found, the file will be stored in the root folder.
+ *        <li><code>metadata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this track in. Setting this to 0 will store
+ *        the track on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_Track_From_File_Descriptor()
+ * @see LIBMTP_Send_File_From_File()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device,
+			 char const * const path, LIBMTP_track_t * const metadata,
+                         LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  int fd;
+  int ret;
+
+  // Sanity check
+  if (path == NULL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Track_From_File(): Bad arguments, path was NULL.");
+    return -1;
+  }
+
+  // Open file
+#ifdef __WIN32__
+#ifdef USE_WINDOWS_IO_H
+  if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1) ) {
+#else
+  if ( (fd = open(path, O_RDONLY|O_BINARY) == -1) ) {
+#endif
+#else
+  if ( (fd = open(path, O_RDONLY)) == -1) {
+#endif
+    printf("LIBMTP_Send_Track_From_File(): Could not open source file \"%s\"\n", path);
+    return -1;
+  }
+
+  ret = LIBMTP_Send_Track_From_File_Descriptor(device, fd, metadata, callback, data);
+
+  // Close file.
+#ifdef USE_WINDOWS_IO_H
+  _close(fd);
+#else
+  close(fd);
+#endif
+
+  return ret;
+}
+
+/**
+ * This function sends a track from a file descriptor to an
+ * MTP device. A filename and a set of metadata must be
+ * given as input.
+ * @param device a pointer to the device to send the track to.
+ * @param fd the filedescriptor for a local file which will be sent.
+ * @param metadata a track metadata set to be written along with the file.
+ *        After this call the field <code>metadata-&gt;item_id</code>
+ *        will contain the new track ID. Other fields such
+ *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
+ *        or <code>metadata-&gt;storage_id</code> may also change during this 
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>metadata-&gt;parent_id</code> should be set to the parent 
+ *        (e.g. folder) to store this track in. Since some 
+ *        devices are a bit picky about where files
+ *        are placed, a default folder will be chosen if libmtp
+ *        has detected one for the current filetype and this
+ *        parameter is set to 0. If this is 0 and no default folder
+ *        can be found, the file will be stored in the root folder.
+ *        <li><code>metadata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this track in. Setting this to 0 will store
+ *        the track on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_Track_From_File()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
+			 int const fd, LIBMTP_track_t * const metadata,
+                         LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  int subcall_ret;
+  LIBMTP_file_t filedata;
+
+  // Sanity check, is this really a track?
+  if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Send_Track_From_File_Descriptor(): "
+			    "I don't think this is actually a track, strange filetype...");
+  }
+
+  // Wrap around the file transfer function
+  filedata.item_id = metadata->item_id;
+  filedata.parent_id = metadata->parent_id;
+  filedata.storage_id = metadata->storage_id;
+  filedata.filename = metadata->filename;
+  filedata.filesize = metadata->filesize;
+  filedata.filetype = metadata->filetype;
+  filedata.next = NULL;
+
+  subcall_ret = LIBMTP_Send_File_From_File_Descriptor(device,
+						      fd, 
+						      &filedata,
+						      callback,
+						      data);
+
+  if (subcall_ret != 0) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Send_Track_From_File_Descriptor(): "
+			    "subcall to LIBMTP_Send_File_From_File_Descriptor failed.");
+    // We used to delete the file here, but don't... It might be OK after all.
+    // (void) LIBMTP_Delete_Object(device, metadata->item_id);
+    return -1;
+  }
+  
+  // Pick up new item (and parent, storage) ID
+  metadata->item_id = filedata.item_id;
+  metadata->parent_id = filedata.parent_id;
+  metadata->storage_id = filedata.storage_id;
+
+  // Set track metadata for the new fine track
+  subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
+  if (subcall_ret != 0) {
+    // Subcall will add error to errorstack
+    // We used to delete the file here, but don't... It might be OK after all.
+    // (void) LIBMTP_Delete_Object(device, metadata->item_id);
+    return -1;
+  }
+
+  // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
+  // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
+
+  return 0;
+}
+
+/**
+ * This function sends a track from a handler function to an
+ * MTP device. A filename and a set of metadata must be
+ * given as input.
+ * @param device a pointer to the device to send the track to.
+ * @param get_func the function to call when we have data.
+ * @param priv the user-defined pointer that is passed to
+ *             <code>get_func</code>.
+ * @param metadata a track metadata set to be written along with the file.
+ *        After this call the field <code>metadata-&gt;item_id</code>
+ *        will contain the new track ID. Other fields such
+ *        as the <code>metadata-&gt;filename</code>, <code>metadata-&gt;parent_id</code>
+ *        or <code>metadata-&gt;storage_id</code> may also change during this 
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>metadata-&gt;parent_id</code> should be set to the parent 
+ *        (e.g. folder) to store this track in. Since some 
+ *        devices are a bit picky about where files
+ *        are placed, a default folder will be chosen if libmtp
+ *        has detected one for the current filetype and this
+ *        parameter is set to 0. If this is 0 and no default folder
+ *        can be found, the file will be stored in the root folder.
+ *        <li><code>metadata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this track in. Setting this to 0 will store
+ *        the track on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_Track_From_File()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_Track_From_Handler(LIBMTP_mtpdevice_t *device,
+			 MTPDataGetFunc get_func, void * priv, LIBMTP_track_t * const metadata,
+                         LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  int subcall_ret;
+  LIBMTP_file_t filedata;
+
+  // Sanity check, is this really a track?
+  if (!LIBMTP_FILETYPE_IS_TRACK(metadata->filetype)) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Send_Track_From_Handler(): "
+			    "I don't think this is actually a track, strange filetype...");
+  }
+
+  // Wrap around the file transfer function
+  filedata.item_id = metadata->item_id;
+  filedata.parent_id = metadata->parent_id;
+  filedata.storage_id = metadata->storage_id;
+  filedata.filename = metadata->filename;
+  filedata.filesize = metadata->filesize;
+  filedata.filetype = metadata->filetype;
+  filedata.next = NULL;
+
+  subcall_ret = LIBMTP_Send_File_From_Handler(device,
+						      get_func,
+                  priv, 
+						      &filedata,
+						      callback,
+						      data);
+
+  if (subcall_ret != 0) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, 
+			    "LIBMTP_Send_Track_From_Handler(): "
+			    "subcall to LIBMTP_Send_File_From_Handler failed.");
+    // We used to delete the file here, but don't... It might be OK after all.
+    // (void) LIBMTP_Delete_Object(device, metadata->item_id);
+    return -1;
+  }
+
+  // Pick up new item (and parent, storage) ID
+  metadata->item_id = filedata.item_id;
+  metadata->parent_id = filedata.parent_id;
+  metadata->storage_id = filedata.storage_id;
+
+  // Set track metadata for the new fine track
+  subcall_ret = LIBMTP_Update_Track_Metadata(device, metadata);
+  if (subcall_ret != 0) {
+    // Subcall will add error to errorstack
+    // We used to delete the file here, but don't... It might be OK after all.
+    // (void) LIBMTP_Delete_Object(device, metadata->item_id);
+    return -1;
+  }
+
+  // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor
+  // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata.
+
+  return 0;
+}
+
+/**
+ * This function sends a local file to an MTP device.
+ * A filename and a set of metadata must be
+ * given as input.
+ * @param device a pointer to the device to send the track to.
+ * @param path the filename of a local file which will be sent.
+ * @param filedata a file metadata set to be written along with the file.
+ *        After this call the field <code>filedata-&gt;item_id</code>
+ *        will contain the new file ID. Other fields such
+ *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
+ *        or <code>filedata-&gt;storage_id</code> may also change during this
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
+ *        (e.g. folder) to store this file in. If this is 0,
+ *        the file will be stored in the root folder.
+ *        <li><code>filedata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this file in. Setting this to 0 will store
+ *        the file on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_File_From_File_Descriptor()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
+			       char const * const path, LIBMTP_file_t * const filedata,
+			       LIBMTP_progressfunc_t const callback,
+			       void const * const data)
+{
+  int fd;
+  int ret;
+
+  // Sanity check
+  if (path == NULL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Bad arguments, path was NULL.");
+    return -1;
+  }
+
+  // Open file
+#ifdef __WIN32__
+#ifdef USE_WINDOWS_IO_H
+  if ( (fd = _open(path, O_RDONLY|O_BINARY) == -1) ) {
+#else
+  if ( (fd = open(path, O_RDONLY|O_BINARY) == -1) ) {
+#endif
+#else
+  if ( (fd = open(path, O_RDONLY)) == -1) {
+#endif
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Could not open source file.");
+    return -1;
+  }
+
+  ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data);
+
+  // Close file.
+#ifdef USE_WINDOWS_IO_H
+  _close(fd);
+#else
+  close(fd);
+#endif
+
+  return ret;
+}
+
+/**
+ * This function sends a generic file from a file descriptor to an
+ * MTP device. A filename and a set of metadata must be
+ * given as input.
+ *
+ * This can potentially be used for sending in a stream of unknown
+ * length. Send music files with
+ * <code>LIBMTP_Send_Track_From_File_Descriptor()</code>
+ *
+ * @param device a pointer to the device to send the file to.
+ * @param fd the filedescriptor for a local file which will be sent.
+ * @param filedata a file metadata set to be written along with the file.
+ *        After this call the field <code>filedata-&gt;item_id</code>
+ *        will contain the new file ID. Other fields such
+ *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
+ *        or <code>filedata-&gt;storage_id</code> may also change during this
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
+ *        (e.g. folder) to store this file in. If this is 0,
+ *        the file will be stored in the root folder.
+ *        <li><code>filedata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this file in. Setting this to 0 will store
+ *        the file on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_File_From_File()
+ * @see LIBMTP_Send_Track_From_File_Descriptor()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
+			 int const fd, LIBMTP_file_t * const filedata,
+                         LIBMTP_progressfunc_t const callback,
+			 void const * const data)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  LIBMTP_file_t *newfilemeta;
+
+  if (send_file_object_info(device, filedata))
+  {
+    // no need to output an error since send_file_object_info will already have done so
+    return -1;
+  }
+
+  // Callbacks
+  ptp_usb->callback_active = 1;
+  // The callback will deactivate itself after this amount of data has been sent
+  // One BULK header for the request, one for the data phase. No parameters to the request.
+  ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
+  ptp_usb->current_transfer_complete = 0;
+  ptp_usb->current_transfer_callback = callback;
+  ptp_usb->current_transfer_callback_data = data;
+
+  ret = ptp_sendobject_fromfd(params, fd, filedata->filesize);
+
+  ptp_usb->callback_active = 0;
+  ptp_usb->current_transfer_callback = NULL;
+  ptp_usb->current_transfer_callback_data = NULL;
+
+  if (ret == PTP_ERROR_CANCEL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer.");
+    return -1;
+  }
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): "
+				"Could not send object.");
+    return -1;
+  }
+
+  add_object_to_cache(device, filedata->item_id);
+
+  /*
+   * Get the device-assined parent_id from the cache.
+   * The operation that adds it to the cache will
+   * look it up from the device, so we get the new
+   * parent_id from the cache.
+   */
+  newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
+  if (newfilemeta != NULL) {
+    filedata->parent_id = newfilemeta->parent_id;
+    filedata->storage_id = newfilemeta->storage_id;
+    LIBMTP_destroy_file_t(newfilemeta);
+  } else {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
+			    "LIBMTP_Send_File_From_File_Descriptor(): "
+			    "Could not retrieve updated metadata.");
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * This function sends a generic file from a handler function to an
+ * MTP device. A filename and a set of metadata must be
+ * given as input.
+ *
+ * This can potentially be used for sending in a stream of unknown
+ * length. Send music files with
+ * <code>LIBMTP_Send_Track_From_Handler()</code>
+ *
+ * @param device a pointer to the device to send the file to.
+ * @param get_func the function to call to get data to write
+ * @param priv a user-defined pointer that is passed along to
+ *        <code>get_func</code>. If not used, this is set to NULL.
+ * @param filedata a file metadata set to be written along with the file.
+ *        After this call the field <code>filedata-&gt;item_id</code>
+ *        will contain the new file ID. Other fields such
+ *        as the <code>filedata-&gt;filename</code>, <code>filedata-&gt;parent_id</code>
+ *        or <code>filedata-&gt;storage_id</code> may also change during this
+ *        operation due to device restrictions, so do not rely on the
+ *        contents of this struct to be preserved in any way.
+ *        <ul>
+ *        <li><code>filedata-&gt;parent_id</code> should be set to the parent
+ *        (e.g. folder) to store this file in. If this is 0,
+ *        the file will be stored in the root folder.
+ *        <li><code>filedata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this file in. Setting this to 0 will store
+ *        the file on the primary storage.
+ *        </ul>
+ * @param callback a progress indicator function or NULL to ignore.
+ * @param data a user-defined pointer that is passed along to
+ *             the <code>progress</code> function in order to
+ *             pass along some user defined data to the progress
+ *             updates. If not used, set this to NULL.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ * @see LIBMTP_Send_File_From_File()
+ * @see LIBMTP_Send_Track_From_File_Descriptor()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Send_File_From_Handler(LIBMTP_mtpdevice_t *device,
+			 MTPDataGetFunc get_func, void * priv, LIBMTP_file_t * const filedata,
+       LIBMTP_progressfunc_t const callback, void const * const data)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  LIBMTP_file_t *newfilemeta;
+
+  if (send_file_object_info(device, filedata))
+  {
+    // no need to output an error since send_file_object_info will already have done so
+    return -1;
+  }
+
+  // Callbacks
+  ptp_usb->callback_active = 1;
+  // The callback will deactivate itself after this amount of data has been sent
+  // One BULK header for the request, one for the data phase. No parameters to the request.
+  ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
+  ptp_usb->current_transfer_complete = 0;
+  ptp_usb->current_transfer_callback = callback;
+  ptp_usb->current_transfer_callback_data = data;
+
+  MTPDataHandler mtp_handler;
+  mtp_handler.getfunc = get_func;
+  mtp_handler.putfunc = NULL;
+  mtp_handler.priv = priv;
+
+  PTPDataHandler handler;
+  handler.getfunc = get_func_wrapper;
+  handler.putfunc = NULL;
+  handler.priv = &mtp_handler;
+
+  ret = ptp_sendobject_from_handler(params, &handler, filedata->filesize);
+
+  ptp_usb->callback_active = 0;
+  ptp_usb->current_transfer_callback = NULL;
+  ptp_usb->current_transfer_callback_data = NULL;
+
+  if (ret == PTP_ERROR_CANCEL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_Handler(): Cancelled transfer.");
+    return -1;
+  }
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_Handler(): "
+				"Could not send object.");
+    return -1;
+  }
+
+  add_object_to_cache(device, filedata->item_id);
+
+  /*
+   * Get the device-assined parent_id from the cache.
+   * The operation that adds it to the cache will
+   * look it up from the device, so we get the new
+   * parent_id from the cache.
+   */
+  newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
+  if (newfilemeta != NULL) {
+    filedata->parent_id = newfilemeta->parent_id;
+    filedata->storage_id = newfilemeta->storage_id;
+    LIBMTP_destroy_file_t(newfilemeta);
+  } else {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
+			    "LIBMTP_Send_File_From_Handler(): "
+			    "Could not retrieve updated metadata.");
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * This function sends the file object info, ready for sendobject
+ * @param device a pointer to the device to send the file to.
+ * @param filedata a file metadata set to be written along with the file.
+ * @return 0 if the transfer was successful, any other value means
+ *           failure.
+ */
+static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *filedata)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint32_t store;
+  int use_primary_storage = 1;
+  uint16_t of = map_libmtp_type_to_ptp_type(filedata->filetype);
+  LIBMTP_devicestorage_t *storage;
+  uint32_t localph = filedata->parent_id;
+  uint16_t ret;
+  int i;
+
+  if (filedata->storage_id != 0) {
+    store = filedata->storage_id;
+  } else {
+    store = get_writeable_storageid(device, filedata->filesize);
+  }
+  // Detect if something non-primary is in use.
+  storage = device->storage;
+  if (storage != NULL && store != storage->id) {
+    use_primary_storage = 0;
+  }
+
+  /*
+   * If no destination folder was given, look up a default
+   * folder if possible. Perhaps there is some way of retrieveing
+   * the default folder for different forms of content, what
+   * do I know, we use a fixed list in lack of any better method.
+   * Some devices obviously need to have their files in certain
+   * folders in order to find/display them at all (hello Creative),
+   * so we have to have a method for this. We only do this if the
+   * primary storage is in use.
+   */
+
+  if (localph == 0 && use_primary_storage) {
+    if (LIBMTP_FILETYPE_IS_AUDIO(filedata->filetype)) {
+      localph = device->default_music_folder;
+    } else if (LIBMTP_FILETYPE_IS_VIDEO(filedata->filetype)) {
+      localph = device->default_video_folder;
+    } else if (of == PTP_OFC_EXIF_JPEG ||
+	       of == PTP_OFC_JP2 ||
+	       of == PTP_OFC_JPX ||
+	       of == PTP_OFC_JFIF ||
+	       of == PTP_OFC_TIFF ||
+	       of == PTP_OFC_TIFF_IT ||
+	       of == PTP_OFC_BMP ||
+	       of == PTP_OFC_GIF ||
+	       of == PTP_OFC_PICT ||
+	       of == PTP_OFC_PNG ||
+	       of == PTP_OFC_MTP_WindowsImageFormat) {
+      localph = device->default_picture_folder;
+    } else if (of == PTP_OFC_MTP_vCalendar1 ||
+	       of == PTP_OFC_MTP_vCalendar2 ||
+	       of == PTP_OFC_MTP_UndefinedContact ||
+	       of == PTP_OFC_MTP_vCard2 ||
+	       of == PTP_OFC_MTP_vCard3 ||
+	       of == PTP_OFC_MTP_UndefinedCalendarItem) {
+      localph = device->default_organizer_folder;
+    } else if (of == PTP_OFC_Text) {
+      localph = device->default_text_folder;
+    }
+  }
+
+  // Here we wire the type to unknown on bugged, but
+  // Ogg or FLAC-supportive devices.
+  if (FLAG_OGG_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_OGG) {
+    of = PTP_OFC_Undefined;
+  }
+  if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && of == PTP_OFC_MTP_FLAC) {
+    of = PTP_OFC_Undefined;
+  }
+
+  if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
+      !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
+    /*
+     * MTP enhanched does it this way (from a sniff):
+     * -> PTP_OC_MTP_SendObjectPropList (0x9808):
+     *    20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
+     *    FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
+     *    Length: 0x00000020
+     *    Type:   0x0001 PTP_USB_CONTAINER_COMMAND
+     *    Code:   0x9808
+     *    Transaction ID: 0x0000001B
+     *    Param1: 0x00010001 <- store
+     *    Param2: 0xffffffff <- parent handle (-1 ?)
+     *    Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
+     *    Param4: 0x00000000 <- file length MSB (-0x0c header len)
+     *    Param5: 0x00005e12 <- file length LSB (-0x0c header len)
+     *
+     * -> PTP_OC_MTP_SendObjectPropList (0x9808):
+     *    46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
+     *    00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
+     *    00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
+     *    00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
+     *    00 4F DC 02 00 01                               - dc4f = non consumable
+     *    Length: 0x00000046
+     *    Type:   0x0002 PTP_USB_CONTAINER_DATA
+     *    Code:   0x9808
+     *    Transaction ID: 0x0000001B
+     *    Metadata....
+     *    0x00000003 <- Number of metadata items
+     *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
+     *    0xdc07     <- metadata type: file name
+     *    0xffff     <- metadata type: string
+     *    0x0d       <- number of (uint16_t) characters
+     *    4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
+     *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
+     *    0xdc03     <- metadata type: protection status
+     *    0x0004     <- metadata type: uint16_t
+     *    0x0000     <- not protected
+     *    0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
+     *    0xdc4f     <- non consumable
+     *    0x0002     <- metadata type: uint8_t
+     *    0x01       <- non-consumable (this device cannot display PDF)
+     *
+     * <- Read 0x18 bytes back
+     *    18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
+     *    00 00 00 00 01 40 00 00
+     *    Length: 0x000000018
+     *    Type:   0x0003 PTP_USB_CONTAINER_RESPONSE
+     *    Code:   0x2001 PTP_OK
+     *    Transaction ID: 0x0000001B
+     *    Param1: 0x00010001 <- store
+     *    Param2: 0x00000000 <- parent handle
+     *    Param3: 0x00004001 <- new file/object ID
+     *
+     * -> PTP_OC_SendObject (0x100d)
+     *    0C 00 00 00 01 00 0D 10 1C 00 00 00
+     * -> ... all the bytes ...
+     * <- Read 0x0c bytes back
+     *    0C 00 00 00 03 00 01 20 1C 00 00 00
+     *    ... Then update metadata one-by one, actually (instead of sending it first!) ...
+     */
+    MTPProperties *props = NULL;
+    int nrofprops = 0;
+    MTPProperties *prop = NULL;
+    uint16_t *properties = NULL;
+    uint32_t propcnt = 0;
+
+    // default parent handle
+    if (localph == 0)
+      localph = 0xFFFFFFFFU; // Set to -1
+
+    // Must be 0x00000000U for new objects
+    filedata->item_id = 0x00000000U;
+
+    ret = ptp_mtp_getobjectpropssupported(params, of, &propcnt, &properties);
+
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], of, &opd);
+      if (ret != PTP_RC_OK) {
+	add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_ObjectFileName:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = filedata->item_id;
+	  prop->property = PTP_OPC_ObjectFileName;
+	  prop->datatype = PTP_DTC_STR;
+	  if (filedata->filename != NULL) {
+	    prop->propval.str = strdup(filedata->filename);
+	    if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+	      strip_7bit_from_utf8(prop->propval.str);
+	    }
+	  }
+	  break;
+	case PTP_OPC_ProtectionStatus:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = filedata->item_id;
+	  prop->property = PTP_OPC_ProtectionStatus;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = 0x0000U; /* Not protected */
+	  break;
+	case PTP_OPC_NonConsumable:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = filedata->item_id;
+	  prop->property = PTP_OPC_NonConsumable;
+	  prop->datatype = PTP_DTC_UINT8;
+	  prop->propval.u8 = 0x00; /* It is supported, then it is consumable */
+	  break;
+	case PTP_OPC_Name:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = filedata->item_id;
+	  prop->property = PTP_OPC_Name;
+	  prop->datatype = PTP_DTC_STR;
+	  if (filedata->filename != NULL)
+	    prop->propval.str = strdup(filedata->filename);
+	  break;
+	case PTP_OPC_DateModified:
+	  // Tag with current time if that is supported
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = filedata->item_id;
+	    prop->property = PTP_OPC_DateModified;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = get_iso8601_stamp();
+	    filedata->modificationdate = time(NULL);
+	  }
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+    free(properties);
+
+    ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id,
+				     of, filedata->filesize, props, nrofprops);
+
+    /* Free property list */
+    ptp_destroy_object_prop_list(props, nrofprops);
+
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "send_file_object_info():"
+				  "Could not send object property list.");
+      if (ret == PTP_RC_AccessDenied) {
+	add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
+      }
+      return -1;
+    }
+  } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
+    PTPObjectInfo new_file;
+
+    memset(&new_file, 0, sizeof(PTPObjectInfo));
+
+    new_file.Filename = filedata->filename;
+    if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+      strip_7bit_from_utf8(new_file.Filename);
+    }
+    // We lose precision here.
+    new_file.ObjectCompressedSize = (uint32_t) filedata->filesize;
+    new_file.ObjectFormat = of;
+    new_file.StorageID = store;
+    new_file.ParentObject = localph;
+    new_file.ModificationDate = time(NULL);
+
+    // Create the object
+    ret = ptp_sendobjectinfo(params, &store, &localph, &filedata->item_id, &new_file);
+
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "send_file_object_info(): "
+				  "Could not send object info.");
+      if (ret == PTP_RC_AccessDenied) {
+	add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
+      }
+      return -1;
+    }
+    // NOTE: the char* pointers inside new_file are not copies so don't
+    // try to destroy this objectinfo!
+  }
+
+  // Now there IS an object with this parent handle.
+  filedata->parent_id = localph;
+
+  return 0;
+}
+
+/**
+ * This function updates the MTP track object metadata on a
+ * single file identified by an object ID.
+ * @param device a pointer to the device to update the track
+ *        metadata on.
+ * @param metadata a track metadata set to be written to the file.
+ *        notice that the <code>track_id</code> field of the
+ *        metadata structure must be correct so that the
+ *        function can update the right file. If some properties
+ *        of this metadata are set to NULL (strings) or 0
+ *        (numerical values) they will be discarded and the
+ *        track will not be tagged with these blank values.
+ * @return 0 on success, any other value means failure. If some
+ *        or all of the properties fail to update we will still
+ *        return success. On some devices (notably iRiver T30)
+ *        properties that exist cannot be updated.
+ */
+int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *device,
+				 LIBMTP_track_t const * const metadata)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint32_t i;
+  uint16_t *properties = NULL;
+  uint32_t propcnt = 0;
+
+  // First see which properties can be set on this file format and apply accordingly
+  // i.e only try to update this metadata for object tags that exist on the current player.
+  ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &properties);
+  if (ret != PTP_RC_OK) {
+    // Just bail out for now, nothing is ever set.
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+			    "could not retrieve supported object properties.");
+    return -1;
+  }
+  if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
+      !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
+    MTPProperties *props = NULL;
+    MTPProperties *prop = NULL;
+    int nrofprops = 0;
+
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
+      if (ret != PTP_RC_OK) {
+	add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_Name:
+	  if (metadata->title == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Name;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->title);
+	  break;
+	case PTP_OPC_AlbumName:
+	  if (metadata->album == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_AlbumName;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->album);
+	  break;
+	case PTP_OPC_Artist:
+	  if (metadata->artist == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Artist;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->artist);
+	  break;
+	case PTP_OPC_Composer:
+	  if (metadata->composer == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Composer;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->composer);
+	  break;
+	case PTP_OPC_Genre:
+	  if (metadata->genre == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Genre;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->genre);
+	  break;
+	case PTP_OPC_Duration:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Duration;
+	  prop->datatype = PTP_DTC_UINT32;
+	  prop->propval.u32 = adjust_u32(metadata->duration, &opd);
+	  break;
+	case PTP_OPC_Track:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Track;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = adjust_u16(metadata->tracknumber, &opd);
+	  break;
+	case PTP_OPC_OriginalReleaseDate:
+	  if (metadata->date == NULL)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_OriginalReleaseDate;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(metadata->date);
+	  break;
+	case PTP_OPC_SampleRate:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_SampleRate;
+	  prop->datatype = PTP_DTC_UINT32;
+	  prop->propval.u32 = adjust_u32(metadata->samplerate, &opd);
+	  break;
+	case PTP_OPC_NumberOfChannels:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_NumberOfChannels;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = adjust_u16(metadata->nochannels, &opd);
+	  break;
+	case PTP_OPC_AudioWAVECodec:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_AudioWAVECodec;
+	  prop->datatype = PTP_DTC_UINT32;
+	  prop->propval.u32 = adjust_u32(metadata->wavecodec, &opd);
+	  break;
+	case PTP_OPC_AudioBitRate:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_AudioBitRate;
+	  prop->datatype = PTP_DTC_UINT32;
+	  prop->propval.u32 = adjust_u32(metadata->bitrate, &opd);
+	  break;
+	case PTP_OPC_BitRateType:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_BitRateType;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = adjust_u16(metadata->bitratetype, &opd);
+	  break;
+	case PTP_OPC_Rating:
+	  // TODO: shall this be set for rating 0?
+	  if (metadata->rating == 0)
+	    break;
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_Rating;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = adjust_u16(metadata->rating, &opd);
+	  break;
+	case PTP_OPC_UseCount:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = metadata->item_id;
+	  prop->property = PTP_OPC_UseCount;
+	  prop->datatype = PTP_DTC_UINT32;
+	  prop->propval.u32 = adjust_u32(metadata->usecount, &opd);
+	  break;
+	case PTP_OPC_DateModified:
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    // Tag with current time if that is supported
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = metadata->item_id;
+	    prop->property = PTP_OPC_DateModified;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = get_iso8601_stamp();
+	  }
+	  break;
+	default:
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+
+    // NOTE: File size is not updated, this should not change anyway.
+    // neither will we change the filename.
+
+    ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
+
+    ptp_destroy_object_prop_list(props, nrofprops);
+
+    if (ret != PTP_RC_OK) {
+      // TODO: return error of which property we couldn't set
+      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+			      "could not set object property list.");
+      free(properties);
+      return -1;
+    }
+
+  } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd);
+      if (ret != PTP_RC_OK) {
+	add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_Name:
+	  // Update title
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track title.");
+	  }
+	  break;
+	case PTP_OPC_AlbumName:
+	  // Update album
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_AlbumName, metadata->album);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track album name.");
+	  }
+	  break;
+	case PTP_OPC_Artist:
+	  // Update artist
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_Artist, metadata->artist);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track artist name.");
+	  }
+	  break;
+	case PTP_OPC_Composer:
+	  // Update composer
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_Composer, metadata->composer);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track composer name.");
+	  }
+	  break;
+	case PTP_OPC_Genre:
+	  // Update genre
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_Genre, metadata->genre);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track genre name.");
+	  }
+	  break;
+	case PTP_OPC_Duration:
+	  // Update duration
+	  if (metadata->duration != 0) {
+	    ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, adjust_u32(metadata->duration, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set track duration.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_Track:
+	  // Update track number.
+	  if (metadata->tracknumber != 0) {
+	    ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, adjust_u16(metadata->tracknumber, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set track tracknumber.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_OriginalReleaseDate:
+	  // Update creation datetime
+	  ret = set_object_string(device, metadata->item_id, PTP_OPC_OriginalReleaseDate, metadata->date);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set track release date.");
+	  }
+	  break;
+	  // These are, well not so important.
+	case PTP_OPC_SampleRate:
+	  // Update sample rate
+	  if (metadata->samplerate != 0) {
+	    ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, adjust_u32(metadata->samplerate, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set samplerate.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_NumberOfChannels:
+	  // Update number of channels
+	  if (metadata->nochannels != 0) {
+	    ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, adjust_u16(metadata->nochannels, &opd));
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				    "could not set number of channels.");
+	  }
+	}
+	  break;
+	case PTP_OPC_AudioWAVECodec:
+	  // Update WAVE codec
+	  if (metadata->wavecodec != 0) {
+	    ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, adjust_u32(metadata->wavecodec, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set WAVE codec.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_AudioBitRate:
+	  // Update bitrate
+	  if (metadata->bitrate != 0) {
+	    ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, adjust_u32(metadata->bitrate, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set bitrate.");
+	  }
+	  }
+	  break;
+	case PTP_OPC_BitRateType:
+	  // Update bitrate type
+	  if (metadata->bitratetype != 0) {
+	    ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, adjust_u16(metadata->bitratetype, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set bitratetype.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_Rating:
+	  // Update user rating
+	  // TODO: shall this be set for rating 0?
+	  if (metadata->rating != 0) {
+	    ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, adjust_u16(metadata->rating, &opd));
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set user rating.");
+	    }
+	  }
+	  break;
+	case PTP_OPC_UseCount:
+	  // Update use count, set even to zero if desired.
+	  ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, adjust_u32(metadata->usecount, &opd));
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				  "could not set use count.");
+	  }
+	  break;
+	case PTP_OPC_DateModified:
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    // Update modification time if supported
+	    char *tmpstamp = get_iso8601_stamp();
+	    ret = set_object_string(device, metadata->item_id, PTP_OPC_DateModified, tmpstamp);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+				      "could not set modification date.");
+	    }
+	    free(tmpstamp);
+	  }
+	  break;
+
+	  // NOTE: File size is not updated, this should not change anyway.
+	  // neither will we change the filename.
+	default:
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+  } else {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): "
+                            "Your device doesn't seem to support any known way of setting metadata.");
+    free(properties);
+    return -1;
+  }
+
+  // update cached object properties if metadata cache exists
+  update_metadata_cache(device, metadata->item_id);
+
+  free(properties);
+
+  return 0;
+}
+
+/**
+ * This function deletes a single file, track, playlist, folder or
+ * any other object off the MTP device, identified by the object ID.
+ *
+ * If you delete a folder, there is no guarantee that the device will
+ * really delete all the files that were in that folder, rather it is
+ * expected that they will not be deleted, and will turn up in object
+ * listings with parent set to a non-existant object ID. The safe way
+ * to do this is to recursively delete all files (and folders) contained
+ * in the folder, then the folder itself.
+ *
+ * @param device a pointer to the device to delete the object from.
+ * @param object_id the object to delete.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
+			 uint32_t object_id)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+
+  ret = ptp_deleteobject(params, object_id, 0);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Delete_Object(): could not delete object.");
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * Internal function to update an object filename property.
+ */
+static int set_object_filename(LIBMTP_mtpdevice_t *device,
+			       uint32_t object_id, uint16_t ptp_type,
+			       const char **newname_ptr)
+{
+  PTPParams             *params = (PTPParams *) device->params;
+  PTP_USB               *ptp_usb = (PTP_USB*) device->usbinfo;
+  PTPObjectPropDesc     opd;
+  uint16_t              ret;
+  char                  *newname;
+
+  // See if we can modify the filename on this kind of files.
+  ret = ptp_mtp_getobjectpropdesc(params, PTP_OPC_ObjectFileName, ptp_type, &opd);
+  if (ret != PTP_RC_OK) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
+			    "could not get property description.");
+    return -1;
+  }
+
+  if (!opd.GetSet) {
+    ptp_free_objectpropdesc(&opd);
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
+            " property is not settable.");
+    // TODO: we COULD actually upload/download the object here, if we feel
+    //       like wasting time for the user.
+    return -1;
+  }
+
+  newname = strdup(*newname_ptr);
+
+  if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+    strip_7bit_from_utf8(newname);
+  }
+
+  if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList) &&
+      !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
+    MTPProperties *props = NULL;
+    MTPProperties *prop = NULL;
+    int nrofprops = 0;
+
+    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+    prop->ObjectHandle = object_id;
+    prop->property = PTP_OPC_ObjectFileName;
+    prop->datatype = PTP_DTC_STR;
+    prop->propval.str = newname;
+
+    ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
+
+    ptp_destroy_object_prop_list(props, nrofprops);
+
+    if (ret != PTP_RC_OK) {
+        add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
+              " could not set object property list.");
+        ptp_free_objectpropdesc(&opd);
+        return -1;
+    }
+  } else if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjectPropValue)) {
+    ret = set_object_string(device, object_id, PTP_OPC_ObjectFileName, newname);
+    if (ret != 0) {
+      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
+              " could not set object filename.");
+      ptp_free_objectpropdesc(&opd);
+      return -1;
+    }
+  } else {
+    free(newname);
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_filename(): "
+              " your device doesn't seem to support any known way of setting metadata.");
+    ptp_free_objectpropdesc(&opd);
+    return -1;
+  }
+
+  ptp_free_objectpropdesc(&opd);
+
+  // update cached object properties if metadata cache exists
+  update_metadata_cache(device, object_id);
+
+  return 0;
+}
+
+/**
+ * This function renames a single file.
+ * This simply means that the PTP_OPC_ObjectFileName property
+ * is updated, if this is supported by the device.
+ *
+ * @param device a pointer to the device that contains the file.
+ * @param file the file metadata of the file to rename.
+ *        On success, the filename member is updated. Be aware, that
+ *        this name can be different than newname depending of device restrictions.
+ * @param newname the new filename for this object.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Set_File_Name(LIBMTP_mtpdevice_t *device,
+                   LIBMTP_file_t *file, const char *newname)
+{
+  int         ret;
+
+  ret = set_object_filename(device, file->item_id,
+			    map_libmtp_type_to_ptp_type(file->filetype),
+			    &newname);
+
+  if (ret != 0) {
+    return ret;
+  }
+
+  free(file->filename);
+  file->filename = strdup(newname);
+  return ret;
+}
+
+/**
+ * This function renames a single folder.
+ * This simply means that the PTP_OPC_ObjectFileName property
+ * is updated, if this is supported by the device.
+ *
+ * @param device a pointer to the device that contains the file.
+ * @param folder the folder metadata of the folder to rename.
+ *        On success, the name member is updated. Be aware, that
+ *        this name can be different than newname depending of device restrictions.
+ * @param newname the new name for this object.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Set_Folder_Name(LIBMTP_mtpdevice_t *device,
+                   LIBMTP_folder_t *folder, const char* newname)
+{
+  int ret;
+
+  ret = set_object_filename(device, folder->folder_id,
+			    PTP_OFC_Association,
+			    &newname);
+
+  if (ret != 0) {
+    return ret;
+    }
+
+  free(folder->name);
+  folder->name = strdup(newname);
+  return ret;
+}
+
+/**
+ * This function renames a single track.
+ * This simply means that the PTP_OPC_ObjectFileName property
+ * is updated, if this is supported by the device.
+ *
+ * @param device a pointer to the device that contains the file.
+ * @param track the track metadata of the track to rename.
+ *        On success, the filename member is updated. Be aware, that
+ *        this name can be different than newname depending of device restrictions.
+ * @param newname the new filename for this object.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Set_Track_Name(LIBMTP_mtpdevice_t *device,
+                   LIBMTP_track_t *track, const char* newname)
+{
+  int         ret;
+
+  ret = set_object_filename(device, track->item_id,
+			    map_libmtp_type_to_ptp_type(track->filetype),
+			    &newname);
+
+  if (ret != 0) {
+    return ret;
+  }
+
+  free(track->filename);
+  track->filename = strdup(newname);
+  return ret;
+}
+
+/**
+ * This function renames a single playlist object file holder.
+ * This simply means that the <code>PTP_OPC_ObjectFileName</code>
+ * property is updated, if this is supported by the device.
+ * The playlist filename should nominally end with an extension
+ * like ".pla".
+ *
+ * NOTE: if you want to change the metadata the device display
+ * about a playlist you must <i>not</i> use this function,
+ * use <code>LIBMTP_Update_Playlist()</code> instead!
+ *
+ * @param device a pointer to the device that contains the file.
+ * @param playlist the playlist metadata of the playlist to rename.
+ *        On success, the name member is updated. Be aware, that
+ *        this name can be different than newname depending of device restrictions.
+ * @param newname the new name for this object.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Update_Playlist()
+ */
+int LIBMTP_Set_Playlist_Name(LIBMTP_mtpdevice_t *device,
+                   LIBMTP_playlist_t *playlist, const char* newname)
+{
+  int ret;
+
+  ret = set_object_filename(device, playlist->playlist_id,
+			    PTP_OFC_MTP_AbstractAudioVideoPlaylist,
+			    &newname);
+
+  if (ret != 0) {
+    return ret;
+  }
+
+  free(playlist->name);
+  playlist->name = strdup(newname);
+  return ret;
+}
+
+/**
+ * This function renames a single album.
+ * This simply means that the <code>PTP_OPC_ObjectFileName</code>
+ * property is updated, if this is supported by the device.
+ * The album filename should nominally end with an extension
+ * like ".alb".
+ *
+ * NOTE: if you want to change the metadata the device display
+ * about a playlist you must <i>not</i> use this function,
+ * use <code>LIBMTP_Update_Album()</code> instead!
+ *
+ * @param device a pointer to the device that contains the file.
+ * @param album the album metadata of the album to rename.
+ *        On success, the name member is updated. Be aware, that
+ *        this name can be different than newname depending of device restrictions.
+ * @param newname the new name for this object.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Update_Album()
+ */
+int LIBMTP_Set_Album_Name(LIBMTP_mtpdevice_t *device,
+                   LIBMTP_album_t *album, const char* newname)
+{
+  int ret;
+
+  ret = set_object_filename(device, album->album_id,
+			    PTP_OFC_MTP_AbstractAudioAlbum,
+			    &newname);
+
+  if (ret != 0) {
+    return ret;
+  }
+
+  free(album->name);
+  album->name = strdup(newname);
+  return ret;
+}
+
+/**
+ * THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
+ * NOT TO USE IT.
+ *
+ * @see LIBMTP_Set_File_Name()
+ * @see LIBMTP_Set_Track_Name()
+ * @see LIBMTP_Set_Folder_Name()
+ * @see LIBMTP_Set_Playlist_Name()
+ * @see LIBMTP_Set_Album_Name()
+ */
+int LIBMTP_Set_Object_Filename(LIBMTP_mtpdevice_t *device,
+                   uint32_t object_id, char* newname)
+{
+  int             ret;
+  LIBMTP_file_t   *file;
+
+  file = LIBMTP_Get_Filemetadata(device, object_id);
+
+  if (file == NULL) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Set_Object_Filename(): "
+			    "could not get file metadata for target object.");
+    return -1;
+  }
+
+  ret = set_object_filename(device, object_id, map_libmtp_type_to_ptp_type(file->filetype), (const char **) &newname);
+
+  free(file);
+
+  return ret;
+}
+
+/**
+ * Helper function. This indicates if a track exists on the device
+ * @param device a pointer to the device to get the track from.
+ * @param id the track ID of the track to retrieve.
+ * @return TRUE (!=0) if the track exists, FALSE (0) if not
+ */
+int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *device,
+           uint32_t const id)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  PTPObject *ob;
+
+  ret = ptp_object_want (params, id, 0, &ob);
+  if (ret == PTP_RC_OK)
+      return -1;
+  return 0;
+}
+
+/**
+ * This creates a new folder structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_folder_track_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings, e.g.:
+ *
+ * @return a pointer to the newly allocated folder structure.
+ * @see LIBMTP_destroy_folder_t()
+ */
+LIBMTP_folder_t *LIBMTP_new_folder_t(void)
+{
+  LIBMTP_folder_t *new = (LIBMTP_folder_t *) malloc(sizeof(LIBMTP_folder_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->folder_id = 0;
+  new->parent_id = 0;
+  new->storage_id = 0;
+  new->name = NULL;
+  new->sibling = NULL;
+  new->child = NULL;
+  return new;
+}
+
+/**
+ * This recursively deletes the memory for a folder structure.
+ * This shall typically be called on a top-level folder list to
+ * detsroy the entire folder tree.
+ *
+ * @param folder folder structure to destroy
+ * @see LIBMTP_new_folder_t()
+ */
+void LIBMTP_destroy_folder_t(LIBMTP_folder_t *folder)
+{
+
+  if(folder == NULL) {
+     return;
+  }
+
+  //Destroy from the bottom up
+  if(folder->child != NULL) {
+     LIBMTP_destroy_folder_t(folder->child);
+  }
+
+  if(folder->sibling != NULL) {
+    LIBMTP_destroy_folder_t(folder->sibling);
+  }
+
+  if(folder->name != NULL) {
+    free(folder->name);
+  }
+
+  free(folder);
+}
+
+/**
+ * Helper function. Returns a folder structure for a
+ * specified id.
+ *
+ * @param folderlist list of folders to search
+ * @id id of folder to look for
+ * @return a folder or NULL if not found
+ */
+LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t *folderlist, uint32_t id)
+{
+  LIBMTP_folder_t *ret = NULL;
+
+  if(folderlist == NULL) {
+    return NULL;
+  }
+
+  if(folderlist->folder_id == id) {
+    return folderlist;
+  }
+
+  if(folderlist->sibling) {
+    ret = LIBMTP_Find_Folder(folderlist->sibling, id);
+  }
+
+  if(folderlist->child && ret == NULL) {
+    ret = LIBMTP_Find_Folder(folderlist->child, id);
+  }
+
+  return ret;
+}
+
+/**
+ * Function used to recursively get subfolders from params.
+ */
+static LIBMTP_folder_t *get_subfolders_for_folder(LIBMTP_folder_t *list, uint32_t parent)
+{
+  LIBMTP_folder_t *retfolders = NULL, *children, *iter, *curr;
+
+  iter = list->sibling;
+  while(iter != list) {
+    if (iter->parent_id != parent) {
+      iter = iter->sibling;
+      continue;
+    }
+
+    /* We know that iter is a child of 'parent', therefore we can safely
+     * hold on to 'iter' locally since no one else will steal it
+     * from the 'list' as we recurse. */
+    children = get_subfolders_for_folder(list, iter->folder_id);
+
+    curr = iter;
+    iter = iter->sibling;
+
+    // Remove curr from the list.
+    curr->child->sibling = curr->sibling;
+    curr->sibling->child = curr->child;
+
+    // Attach the children to curr.
+    curr->child = children;
+
+    // Put this folder into the list of siblings.
+    curr->sibling = retfolders;
+    retfolders = curr;
+  }
+
+  return retfolders;
+}
+
+/**
+ * This returns a list of all folders available
+ * on the current MTP device.
+ *
+ * @param device a pointer to the device to get the folder listing for.
+ * @return a list of folders
+ */
+LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  LIBMTP_folder_t head, *rv;
+  int i;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  /*
+   * This creates a temporary list of the folders, this is in a
+   * reverse order and uses the Folder pointers that are already
+   * in the Folder structure. From this we can then build up the
+   * folder hierarchy with only looking at this temporary list,
+   * and removing the folders from this temporary list as we go.
+   * This significantly reduces the number of operations that we
+   * have to do in building the folder hierarchy. Also since the
+   * temp list is in reverse order, when we prepend to the sibling
+   * list things are in the same order as they were originally
+   * in the handle list.
+   */
+  head.sibling = &head;
+  head.child = &head;
+  for (i = 0; i < params->nrofobjects; i++) {
+    LIBMTP_folder_t *folder;
+    PTPObject *ob;
+
+    ob = &params->objects[i];
+    if (ob->oi.ObjectFormat != PTP_OFC_Association) {
+      continue;
+    }
+    /*
+     * Do we know how to handle these? They are part
+     * of the MTP 1.0 specification paragraph 3.6.4.
+     * For AssociationDesc 0x00000001U ptp_mtp_getobjectreferences()
+     * should be called on these to get the contained objects, but
+     * we basically don't care. Hopefully parent_id is maintained for all
+     * children, because we rely on that instead.
+     */
+    if (ob->oi.AssociationDesc != 0x00000000U) {
+      printf("MTP extended association type 0x%08x encountered\n", ob->oi.AssociationDesc);
+    }
+
+    // Create a folder struct...
+    folder = LIBMTP_new_folder_t();
+    if (folder == NULL) {
+      // malloc failure or so.
+      return NULL;
+    }
+    folder->folder_id = ob->oid;
+    folder->parent_id = ob->oi.ParentObject;
+    folder->storage_id = ob->oi.StorageID;
+    folder->name = (ob->oi.Filename) ? (char *)strdup(ob->oi.Filename) : NULL;
+
+    // pretend sibling says next, and child says prev.
+    folder->sibling = head.sibling;
+    folder->child = &head;
+    head.sibling->child = folder;
+    head.sibling = folder;
+  }
+
+  // We begin at the root folder and get them all recursively
+  rv = get_subfolders_for_folder(&head, 0x00000000);
+
+  // The temp list should be empty. Clean up any orphans just in case.
+  while(head.sibling != &head) {
+    LIBMTP_folder_t *curr = head.sibling;
+
+    printf("Orphan folder with ID: 0x%08x name: \"%s\" encountered.\n",
+	   curr->folder_id,
+	   curr->name);
+    curr->sibling->child = curr->child;
+    curr->child->sibling = curr->sibling;
+    curr->child = NULL;
+    curr->sibling = NULL;
+    LIBMTP_destroy_folder_t(curr);
+  }
+
+  return rv;
+}
+
+/**
+ * This create a folder on the current MTP device. The PTP name
+ * for a folder is "association". The PTP/MTP devices does not
+ * have an internal "folder" concept really, it contains a flat
+ * list of all files and some file are "associations" that other
+ * files and folders may refer to as its "parent".
+ *
+ * @param device a pointer to the device to create the folder on.
+ * @param name the name of the new folder. Note this can be modified
+ *        if the device does not support all the characters in the
+ *        name.
+ * @param parent_id id of parent folder to add the new folder to,
+ *        or 0 to put it in the root directory.
+ * @param storage_id id of the storage to add this new folder to.
+ *        notice that you cannot mismatch storage id and parent id:
+ *        they must both be on the same storage! Pass in 0 if you
+ *        want to create this folder on the default storage.
+ * @return id to new folder or 0 if an error occured
+ */
+uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name,
+			      uint32_t parent_id, uint32_t storage_id)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint32_t parenthandle = 0;
+  uint32_t store;
+  PTPObjectInfo new_folder;
+  uint16_t ret;
+  uint32_t new_id = 0;
+
+  if (storage_id == 0) {
+    // I'm just guessing that a folder may require 512 bytes
+    store = get_writeable_storageid(device, 512);
+  } else {
+    store = storage_id;
+  }
+  parenthandle = parent_id;
+
+  memset(&new_folder, 0, sizeof(new_folder));
+  new_folder.Filename = name;
+  if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+    strip_7bit_from_utf8(new_folder.Filename);
+  }
+  new_folder.ObjectCompressedSize = 1;
+  new_folder.ObjectFormat = PTP_OFC_Association;
+  new_folder.ProtectionStatus = PTP_PS_NoProtection;
+  new_folder.AssociationType = PTP_AT_GenericFolder;
+  new_folder.ParentObject = parent_id;
+  new_folder.StorageID = store;
+
+  // Create the object
+  // FIXME: use send list here if available.
+  ret = ptp_sendobjectinfo(params, &store, &parenthandle, &new_id, &new_folder);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Create_Folder: Could not send object info.");
+    if (ret == PTP_RC_AccessDenied) {
+      add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
+    }
+    return 0;
+  }
+  // NOTE: don't destroy the new_folder objectinfo, because it is statically referencing
+  // several strings.
+
+  add_object_to_cache(device, new_id);
+
+  return new_id;
+}
+
+/**
+ * This creates a new playlist metadata structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_destroy_playlist_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings, e.g.:
+ *
+ * <pre>
+ * LIBMTP_playlist_t *pl = LIBMTP_new_playlist_t();
+ * pl->name = strdup(str);
+ * ....
+ * LIBMTP_destroy_playlist_t(pl);
+ * </pre>
+ *
+ * @return a pointer to the newly allocated metadata structure.
+ * @see LIBMTP_destroy_playlist_t()
+ */
+LIBMTP_playlist_t *LIBMTP_new_playlist_t(void)
+{
+  LIBMTP_playlist_t *new = (LIBMTP_playlist_t *) malloc(sizeof(LIBMTP_playlist_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->playlist_id = 0;
+  new->parent_id = 0;
+  new->storage_id = 0;
+  new->name = NULL;
+  new->tracks = NULL;
+  new->no_tracks = 0;
+  new->next = NULL;
+  return new;
+}
+
+/**
+ * This destroys a playlist metadata structure and deallocates the memory
+ * used by it, including any strings. Never use a track metadata
+ * structure again after calling this function on it.
+ * @param playlist the playlist metadata to destroy.
+ * @see LIBMTP_new_playlist_t()
+ */
+void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *playlist)
+{
+  if (playlist == NULL) {
+    return;
+  }
+  if (playlist->name != NULL)
+    free(playlist->name);
+  if (playlist->tracks != NULL)
+    free(playlist->tracks);
+  free(playlist);
+  return;
+}
+
+/**
+ * This function returns a list of the playlists available on the
+ * device. Typical usage:
+ *
+ * <pre>
+ * </pre>
+ *
+ * @param device a pointer to the device to get the playlist listing from.
+ * @return a playlist list on success, else NULL. If there are no playlists
+ *         on the device, NULL will be returned as well.
+ * @see LIBMTP_Get_Playlist()
+ */
+LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *device)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
+  PTPParams *params = (PTPParams *) device->params;
+  LIBMTP_playlist_t *retlists = NULL;
+  LIBMTP_playlist_t *curlist = NULL;
+  uint32_t i;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  for (i = 0; i < params->nrofobjects; i++) {
+    LIBMTP_playlist_t *pl;
+    PTPObject *ob;
+    uint16_t ret;
+
+    ob = &params->objects[i];
+
+    // Ignore stuff that isn't playlists
+
+    // For Samsung players we must look for the .spl extension explicitly since
+    // playlists are not stored as playlist objects.
+    if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
+      // Allocate a new playlist type
+      pl = LIBMTP_new_playlist_t();
+      spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
+    }
+    else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
+      continue;
+    }
+    else {
+      // Allocate a new playlist type
+      pl = LIBMTP_new_playlist_t();
+
+      // Try to look up proper name, else use the oi->Filename field.
+      pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
+      if (pl->name == NULL) {
+	pl->name = strdup(ob->oi.Filename);
+      }
+      pl->playlist_id = ob->oid;
+      pl->parent_id = ob->oi.ParentObject;
+      pl->storage_id = ob->oi.StorageID;
+
+      // Then get the track listing for this playlist
+      ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
+      if (ret != PTP_RC_OK) {
+        add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist_List(): "
+				    "could not get object references.");
+        pl->tracks = NULL;
+        pl->no_tracks = 0;
+      }
+    }
+
+    // Add playlist to a list that will be returned afterwards.
+    if (retlists == NULL) {
+      retlists = pl;
+      curlist = pl;
+    } else {
+      curlist->next = pl;
+      curlist = pl;
+    }
+
+    // Call callback here if we decide to add that possibility...
+  }
+  return retlists;
+}
+
+
+/**
+ * This function retrieves an individual playlist from the device.
+ * @param device a pointer to the device to get the playlist from.
+ * @param plid the unique ID of the playlist to retrieve.
+ * @return a valid playlist metadata post or NULL on failure.
+ * @see LIBMTP_Get_Playlist_List()
+ */
+LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *device, uint32_t const plid)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  const int REQ_SPL = FLAG_PLAYLIST_SPL(ptp_usb);
+  PTPParams *params = (PTPParams *) device->params;
+  PTPObject *ob;
+  LIBMTP_playlist_t *pl;
+  uint16_t ret;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0) {
+    flush_handles(device);
+  }
+
+  ret = ptp_object_want (params, plid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK)
+    return NULL;
+
+  // For Samsung players we must look for the .spl extension explicitly since
+  // playlists are not stored as playlist objects.
+  if ( REQ_SPL && is_spl_playlist(&ob->oi) ) {
+    // Allocate a new playlist type
+    pl = LIBMTP_new_playlist_t();
+    spl_to_playlist_t(device, &ob->oi, ob->oid, pl);
+    return pl;
+  }
+
+  // Ignore stuff that isn't playlists
+  else if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioVideoPlaylist ) {
+    return NULL;
+  }
+
+  // Allocate a new playlist type
+  pl = LIBMTP_new_playlist_t();
+
+  pl->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
+  if (pl->name == NULL) {
+    pl->name = strdup(ob->oi.Filename);
+  }
+  pl->playlist_id = ob->oid;
+  pl->parent_id = ob->oi.ParentObject;
+  pl->storage_id = ob->oi.StorageID;
+
+  // Then get the track listing for this playlist
+  ret = ptp_mtp_getobjectreferences(params, pl->playlist_id, &pl->tracks, &pl->no_tracks);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist(): Could not get object references.");
+    pl->tracks = NULL;
+    pl->no_tracks = 0;
+  }
+
+  return pl;
+}
+
+/**
+ * This function creates a new abstract list such as a playlist
+ * or an album.
+ *
+ * @param device a pointer to the device to create the new abstract list
+ *        on.
+ * @param name the name of the new abstract list.
+ * @param artist the artist of the new abstract list or NULL.
+ * @param genre the genre of the new abstract list or NULL.
+ * @param parenthandle the handle of the parent or 0 for no parent
+ *        i.e. the root folder.
+ * @param objectformat the abstract list type to create.
+ * @param suffix the ".foo" (4 characters) suffix to use for the virtual
+ *        "file" created by this operation.
+ * @param newid a pointer to a variable that will hold the new object
+ *        ID if this call is successful.
+ * @param tracks an array of tracks to associate with this list.
+ * @param no_tracks the number of tracks in the list.
+ * @return 0 on success, any other value means failure.
+ */
+static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
+				    char const * const name,
+				    char const * const artist,
+				    char const * const composer,
+				    char const * const genre,
+				    uint32_t const parenthandle,
+				    uint32_t const storageid,
+				    uint16_t const objectformat,
+				    char const * const suffix,
+				    uint32_t * const newid,
+				    uint32_t const * const tracks,
+				    uint32_t const no_tracks)
+
+{
+  int i;
+  int supported = 0;
+  uint16_t ret;
+  uint16_t *properties = NULL;
+  uint32_t propcnt = 0;
+  uint32_t store;
+  uint32_t localph = parenthandle;
+  uint8_t nonconsumable = 0x00U; /* By default it is consumable */
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  char fname[256];
+  uint8_t data[2];
+
+  if (storageid == 0) {
+    // I'm just guessing that an abstract list may require 512 bytes
+    store = get_writeable_storageid(device, 512);
+  } else {
+    store = storageid;
+  }
+
+  // Check if we can create an object of this type
+  for ( i=0; i < params->deviceinfo.ImageFormats_len; i++ ) {
+    if (params->deviceinfo.ImageFormats[i] == objectformat) {
+      supported = 1;
+      break;
+    }
+  }
+  if (!supported) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): player does not support this abstract type.");
+    printf("Unsupported abstract list type: %04x\n", objectformat);
+    return -1;
+  }
+
+  // add the new suffix if it isn't there
+  fname[0] = '\0';
+  if (strlen(name) > strlen(suffix)) {
+    char const * const suff = &name[strlen(name)-strlen(suffix)];
+    if (!strcmp(suff, suffix)) {
+      // Home free.
+      strncpy(fname, name, sizeof(fname));
+    }
+  }
+  // If it didn't end with "<suffix>" then add that here.
+  if (fname[0] == '\0') {
+    strncpy(fname, name, sizeof(fname)-strlen(suffix)-1);
+    strcat(fname, suffix);
+    fname[sizeof(fname)-1] = '\0';
+  }
+
+  if (ptp_operation_issupported(params, PTP_OC_MTP_SendObjectPropList) &&
+      !FLAG_BROKEN_SEND_OBJECT_PROPLIST(ptp_usb)) {
+    MTPProperties *props = NULL;
+    MTPProperties *prop = NULL;
+    int nrofprops = 0;
+
+    *newid = 0x00000000U;
+
+    ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
+
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
+      if (ret != PTP_RC_OK) {
+	add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_ObjectFileName:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = *newid;
+	  prop->property = PTP_OPC_ObjectFileName;
+	  prop->datatype = PTP_DTC_STR;
+	  prop->propval.str = strdup(fname);
+	  if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+	    strip_7bit_from_utf8(prop->propval.str);
+	  }
+	  break;
+	case PTP_OPC_ProtectionStatus:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = *newid;
+	  prop->property = PTP_OPC_ProtectionStatus;
+	  prop->datatype = PTP_DTC_UINT16;
+	  prop->propval.u16 = 0x0000U; /* Not protected */
+	  break;
+	case PTP_OPC_NonConsumable:
+	  prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	  prop->ObjectHandle = *newid;
+	  prop->property = PTP_OPC_NonConsumable;
+	  prop->datatype = PTP_DTC_UINT8;
+	  prop->propval.u8 = nonconsumable;
+	  break;
+	case PTP_OPC_Name:
+	  if (name != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_Name;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(name);
+	  }
+	  break;
+	case PTP_OPC_AlbumArtist:
+	  if (artist != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_AlbumArtist;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(artist);
+	  }
+	  break;
+	case PTP_OPC_Artist:
+	  if (artist != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_Artist;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(artist);
+	  }
+	  break;
+	case PTP_OPC_Composer:
+	  if (composer != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_Composer;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(composer);
+	  }
+	  break;
+	case PTP_OPC_Genre:
+	  if (genre != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_Genre;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(genre);
+	  }
+	  break;
+ 	case PTP_OPC_DateModified:
+	  // Tag with current time if that is supported
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    prop = ptp_get_new_object_prop_entry(&props,&nrofprops);
+	    prop->ObjectHandle = *newid;
+	    prop->property = PTP_OPC_DateModified;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = get_iso8601_stamp();
+	  }
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+    free(properties);
+
+    ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid,
+				     objectformat, 0, props, nrofprops);
+
+    /* Free property list */
+    ptp_destroy_object_prop_list(props, nrofprops);
+
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list.");
+      if (ret == PTP_RC_AccessDenied) {
+	add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
+      }
+      return -1;
+    }
+
+    // now send the blank object
+    ret = ptp_sendobject(params, NULL, 0);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
+      return -1;
+    }
+
+  } else if (ptp_operation_issupported(params,PTP_OC_SendObjectInfo)) {
+    PTPObjectInfo new_object;
+
+    new_object.Filename = fname;
+    if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
+      strip_7bit_from_utf8(new_object.Filename);
+    }
+    new_object.ObjectCompressedSize = 1;
+    new_object.ObjectFormat = objectformat;
+
+    // Create the object
+    ret = ptp_sendobjectinfo(params, &store, &localph, newid, &new_object);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object info (the playlist itself).");
+      if (ret == PTP_RC_AccessDenied) {
+	add_ptp_error_to_errorstack(device, ret, "ACCESS DENIED.");
+      }
+      return -1;
+    }
+    // NOTE: don't destroy new_object objectinfo afterwards - the strings it contains are
+    // not copies.
+    /*
+     * We have to send this one blank data byte.
+     * If we don't, the handle will not be created and thus there is no playlist.
+     */
+    data[0] = '\0';
+    data[1] = '\0';
+    ret = ptp_sendobject(params, data, 1);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send blank object data.");
+      return -1;
+    }
+
+    // set the properties one by one
+    ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
+
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
+      if (ret != PTP_RC_OK) {
+	add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_Name:
+	  if (name != NULL) {
+	    ret = set_object_string(device, *newid, PTP_OPC_Name, name);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity name.");
+	      return -1;
+	    }
+	  }
+	  break;
+	case PTP_OPC_AlbumArtist:
+	  if (artist != NULL) {
+	    ret = set_object_string(device, *newid, PTP_OPC_AlbumArtist, artist);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity album artist.");
+	      return -1;
+	    }
+	  }
+	  break;
+	case PTP_OPC_Artist:
+	  if (artist != NULL) {
+	    ret = set_object_string(device, *newid, PTP_OPC_Artist, artist);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity artist.");
+	      return -1;
+	    }
+	  }
+	  break;
+	case PTP_OPC_Composer:
+	  if (composer != NULL) {
+	    ret = set_object_string(device, *newid, PTP_OPC_Composer, composer);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity composer.");
+	      return -1;
+	    }
+	  }
+	  break;
+	case PTP_OPC_Genre:
+	  if (genre != NULL) {
+	    ret = set_object_string(device, *newid, PTP_OPC_Genre, genre);
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set entity genre.");
+	      return -1;
+	    }
+	  }
+	  break;
+ 	case PTP_OPC_DateModified:
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    ret = set_object_string(device, *newid, PTP_OPC_DateModified, get_iso8601_stamp());
+	    if (ret != 0) {
+	      add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "create_new_abstract_list(): could not set date modified.");
+	      return -1;
+	    }
+	  }
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+    free(properties);
+  }
+
+  if (no_tracks > 0) {
+    // Add tracks to the list as object references.
+    ret = ptp_mtp_setobjectreferences (params, *newid, (uint32_t *) tracks, no_tracks);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): could not add tracks as object references.");
+      return -1;
+    }
+  }
+
+  add_object_to_cache(device, *newid);
+
+  return 0;
+}
+
+/**
+ * This updates the metadata and track listing
+ * for an abstract list.
+ * @param device a pointer to the device that the abstract list
+ *        resides on.
+ * @param name the name of the abstract list.
+ * @param artist the artist of the abstract list or NULL.
+ * @param genre the genre of the abstract list or NULL.
+ * @param objecthandle the object to be updated.
+ * @param objectformat the abstract list type to update.
+ * @param tracks an array of tracks to associate with this list.
+ * @param no_tracks the number of tracks in the list.
+ * @return 0 on success, any other value means failure.
+ */
+static int update_abstract_list(LIBMTP_mtpdevice_t *device,
+				char const * const name,
+				char const * const artist,
+				char const * const composer,
+				char const * const genre,
+				uint32_t const objecthandle,
+				uint16_t const objectformat,
+				uint32_t const * const tracks,
+				uint32_t const no_tracks)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint16_t *properties = NULL;
+  uint32_t propcnt = 0;
+  int i;
+
+  // First see which properties can be set
+  // i.e only try to update this metadata for object tags that exist on the current player.
+  ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties);
+  if (ret != PTP_RC_OK) {
+    // Just bail out for now, nothing is ever set.
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+			    "could not retrieve supported object properties.");
+    return -1;
+  }
+  if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList) &&
+      !FLAG_BROKEN_SET_OBJECT_PROPLIST(ptp_usb)) {
+    MTPProperties *props = NULL;
+    MTPProperties *prop = NULL;
+    int nrofprops = 0;
+
+    for (i=0;i<propcnt;i++) {
+      PTPObjectPropDesc opd;
+
+      ret = ptp_mtp_getobjectpropdesc(params, properties[i], objectformat, &opd);
+      if (ret != PTP_RC_OK) {
+	add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				"could not get property description.");
+      } else if (opd.GetSet) {
+	switch (properties[i]) {
+	case PTP_OPC_Name:
+	  prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	  prop->ObjectHandle = objecthandle;
+	  prop->property = PTP_OPC_Name;
+	  prop->datatype = PTP_DTC_STR;
+	  if (name != NULL)
+	    prop->propval.str = strdup(name);
+	  break;
+	case PTP_OPC_AlbumArtist:
+	  if (artist != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = objecthandle;
+	    prop->property = PTP_OPC_AlbumArtist;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(artist);
+	  }
+	  break;
+	case PTP_OPC_Artist:
+	  if (artist != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = objecthandle;
+	    prop->property = PTP_OPC_Artist;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(artist);
+	  }
+	  break;
+	case PTP_OPC_Composer:
+	  if (composer != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = objecthandle;
+	    prop->property = PTP_OPC_Composer;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(composer);
+	  }
+	  break;
+	case PTP_OPC_Genre:
+	  if (genre != NULL) {
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = objecthandle;
+	    prop->property = PTP_OPC_Genre;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = strdup(genre);
+	  }
+	  break;
+ 	case PTP_OPC_DateModified:
+	  if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	    // Tag with current time if that is supported
+	    prop = ptp_get_new_object_prop_entry(&props, &nrofprops);
+	    prop->ObjectHandle = objecthandle;
+	    prop->property = PTP_OPC_DateModified;
+	    prop->datatype = PTP_DTC_STR;
+	    prop->propval.str = get_iso8601_stamp();
+	  }
+	  break;
+	default:
+	  break;
+	}
+      }
+      ptp_free_objectpropdesc(&opd);
+    }
+
+    // proplist could be NULL if we can't write any properties
+    if (props != NULL) {
+      ret = ptp_mtp_setobjectproplist(params, props, nrofprops);
+
+      ptp_destroy_object_prop_list(props, nrofprops);
+
+      if (ret != PTP_RC_OK) {
+        // TODO: return error of which property we couldn't set
+        add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+                                "could not set object property list.");
+        free(properties);
+        return -1;
+      }
+    }
+
+  } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
+    for (i=0;i<propcnt;i++) {
+      switch (properties[i]) {
+      case PTP_OPC_Name:
+	// Update title
+	ret = set_object_string(device, objecthandle, PTP_OPC_Name, name);
+	if (ret != 0) {
+	  add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				  "could not set title.");
+	}
+	break;
+      case PTP_OPC_AlbumArtist:
+	// Update album artist
+	ret = set_object_string(device, objecthandle, PTP_OPC_AlbumArtist, artist);
+	if (ret != 0) {
+	  add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				  "could not set album artist name.");
+	}
+	break;
+      case PTP_OPC_Artist:
+	// Update artist
+	ret = set_object_string(device, objecthandle, PTP_OPC_Artist, artist);
+	if (ret != 0) {
+	  add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				  "could not set artist name.");
+	}
+      case PTP_OPC_Composer:
+	// Update composer
+	ret = set_object_string(device, objecthandle, PTP_OPC_Composer, composer);
+	if (ret != 0) {
+	  add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				  "could not set composer name.");
+	}
+	break;
+      case PTP_OPC_Genre:
+	// Update genre
+	ret = set_object_string(device, objecthandle, PTP_OPC_Genre, genre);
+	if (ret != 0) {
+	  add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				  "could not set genre.");
+	}
+	break;
+      case PTP_OPC_DateModified:
+	// Update date modified
+	if (!FLAG_CANNOT_HANDLE_DATEMODIFIED(ptp_usb)) {
+	  char *tmpdate = get_iso8601_stamp();
+	  ret = set_object_string(device, objecthandle, PTP_OPC_DateModified, tmpdate);
+	  if (ret != 0) {
+	    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+				    "could not set modification date.");
+	  }
+	  free(tmpdate);
+	}
+      default:
+	break;
+      }
+    }
+  } else {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
+                            "Your device doesn't seem to support any known way of setting metadata.");
+    free(properties);
+    return -1;
+  }
+
+  // Then the object references...
+  ret = ptp_mtp_setobjectreferences (params, objecthandle, (uint32_t *) tracks, no_tracks);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "update_abstract_list(): could not add tracks as object references.");
+    free(properties);
+    return -1;
+  }
+
+  free(properties);
+
+  update_metadata_cache(device, objecthandle);
+
+  return 0;
+}
+
+
+/**
+ * This routine creates a new playlist based on the metadata
+ * supplied. If the <code>tracks</code> field of the metadata
+ * contains a track listing, these tracks will be added to the
+ * playlist.
+ * @param device a pointer to the device to create the new playlist on.
+ * @param metadata the metadata for the new playlist. If the function
+ *        exits with success, the <code>playlist_id</code> field of this
+ *        struct will contain the new playlist ID of the playlist.
+ *        <ul>
+ *        <li><code>metadata-&gt;parent_id</code> should be set to the parent 
+ *        (e.g. folder) to store this track in. Since some 
+ *        devices are a bit picky about where files
+ *        are placed, a default folder will be chosen if libmtp
+ *        has detected one for the current filetype and this
+ *        parameter is set to 0. If this is 0 and no default folder
+ *        can be found, the file will be stored in the root folder.
+ *        <li><code>metadata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this track in. Setting this to 0 will store
+ *        the track on the primary storage.
+ *        </ul>
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Update_Playlist()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *device,
+			       LIBMTP_playlist_t * const metadata)
+{
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  uint32_t localph = metadata->parent_id;
+
+  // Use a default folder if none given
+  if (localph == 0) {
+    if (device->default_playlist_folder != 0)
+      localph = device->default_playlist_folder;
+    else
+      localph = device->default_music_folder;
+  }
+  metadata->parent_id = localph;
+
+  // Samsung needs its own special type of playlists
+  if(FLAG_PLAYLIST_SPL(ptp_usb)) {
+    return playlist_t_to_spl(device, metadata);
+  }
+
+  // Just create a new abstract audio/video playlist...
+  return create_new_abstract_list(device,
+				  metadata->name,
+				  NULL,
+				  NULL,
+				  NULL,
+				  localph,
+				  metadata->storage_id,
+				  PTP_OFC_MTP_AbstractAudioVideoPlaylist,
+				  get_playlist_extension(ptp_usb),
+				  &metadata->playlist_id,
+				  metadata->tracks,
+				  metadata->no_tracks);
+}
+
+/**
+ * This routine updates a playlist based on the metadata
+ * supplied. If the <code>tracks</code> field of the metadata
+ * contains a track listing, these tracks will be added to the
+ * playlist in place of those already present, i.e. the
+ * previous track listing will be deleted. For Samsung devices the
+ * playlist id (metadata->playlist_id) is likely to change.
+ * @param device a pointer to the device to create the new playlist on.
+ * @param metadata the metadata for the playlist to be updated.
+ *                 notice that the field <code>playlist_id</code>
+ *                 must contain the apropriate playlist ID. Playlist ID
+ *                 be modified to a new playlist ID by the time the
+ *                 function returns since edit-in-place is not always possible.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Create_New_Playlist()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device,
+			   LIBMTP_playlist_t * const metadata)
+{
+
+  // Samsung needs its own special type of playlists
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  if(FLAG_PLAYLIST_SPL(ptp_usb)) {
+    return update_spl_playlist(device, metadata);
+  }
+
+  return update_abstract_list(device,
+			      metadata->name,
+			      NULL,
+			      NULL,
+			      NULL,
+			      metadata->playlist_id,
+			      PTP_OFC_MTP_AbstractAudioVideoPlaylist,
+			      metadata->tracks,
+			      metadata->no_tracks);
+}
+
+/**
+ * This creates a new album metadata structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_destroy_album_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings.
+ *
+ * @return a pointer to the newly allocated metadata structure.
+ * @see LIBMTP_destroy_album_t()
+ */
+LIBMTP_album_t *LIBMTP_new_album_t(void)
+{
+  LIBMTP_album_t *new = (LIBMTP_album_t *) malloc(sizeof(LIBMTP_album_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->album_id = 0;
+  new->parent_id = 0;
+  new->storage_id = 0;
+  new->name = NULL;
+  new->artist = NULL;
+  new->composer = NULL;
+  new->genre = NULL;
+  new->tracks = NULL;
+  new->no_tracks = 0;
+  new->next = NULL;
+  return new;
+}
+
+/**
+ * This recursively deletes the memory for an album structure
+ *
+ * @param album structure to destroy
+ * @see LIBMTP_new_album_t()
+ */
+void LIBMTP_destroy_album_t(LIBMTP_album_t *album)
+{
+  if (album == NULL) {
+    return;
+  }
+  if (album->name != NULL)
+    free(album->name);
+  if (album->artist != NULL)
+    free(album->artist);
+  if (album->composer != NULL)
+    free(album->composer);
+  if (album->genre != NULL)
+    free(album->genre);
+  if (album->tracks != NULL)
+    free(album->tracks);
+  free(album);
+  return;
+}
+
+/**
+ * This function maps and copies a property onto the album metadata if applicable.
+ */
+static void pick_property_to_album_metadata(LIBMTP_mtpdevice_t *device,
+					    MTPProperties *prop, LIBMTP_album_t *alb)
+{
+  switch (prop->property) {
+  case PTP_OPC_Name:
+    if (prop->propval.str != NULL)
+      alb->name = strdup(prop->propval.str);
+    else
+      alb->name = NULL;
+    break;
+  case PTP_OPC_AlbumArtist:
+    if (prop->propval.str != NULL) {
+      // This should take precedence over plain "Artist"
+      if (alb->artist != NULL)
+	free(alb->artist);
+      alb->artist = strdup(prop->propval.str);
+    } else
+      alb->artist = NULL;
+    break;
+  case PTP_OPC_Artist:
+    if (prop->propval.str != NULL) {
+      // Only use of AlbumArtist is not set
+      if (alb->artist == NULL)
+	alb->artist = strdup(prop->propval.str);
+    } else
+      alb->artist = NULL;
+    break;
+  case PTP_OPC_Composer:
+    if (prop->propval.str != NULL)
+      alb->composer = strdup(prop->propval.str);
+    else
+      alb->composer = NULL;
+    break;
+  case PTP_OPC_Genre:
+    if (prop->propval.str != NULL)
+      alb->genre = strdup(prop->propval.str);
+    else
+      alb->genre = NULL;
+    break;
+  }
+}
+
+/**
+ * This function retrieves the album metadata for an album
+ * given by a unique ID.
+ * @param device a pointer to the device to get the track metadata off.
+ * @param alb an album metadata metadata set to fill in.
+ */
+static void get_album_metadata(LIBMTP_mtpdevice_t *device,
+			       LIBMTP_album_t *alb)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  uint32_t i;
+  MTPProperties *prop;
+  PTPObject *ob;
+
+  /*
+   * If we have a cached, large set of metadata, then use it!
+   */
+  ret = ptp_object_want(params, alb->album_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
+  if (ob->mtpprops) {
+    prop = ob->mtpprops;
+    for (i=0;i<ob->nrofmtpprops;i++,prop++)
+      pick_property_to_album_metadata(device, prop, alb);
+  } else {
+    uint16_t *props = NULL;
+    uint32_t propcnt = 0;
+
+    // First see which properties can be retrieved for albums
+    ret = ptp_mtp_getobjectpropssupported(params, PTP_OFC_MTP_AbstractAudioAlbum, &propcnt, &props);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "get_album_metadata(): call to ptp_mtp_getobjectpropssupported() failed.");
+      // Just bail out for now, nothing is ever set.
+      return;
+    } else {
+      for (i=0;i<propcnt;i++) {
+	switch (props[i]) {
+	case PTP_OPC_Name:
+	  alb->name = get_string_from_object(device, ob->oid, PTP_OPC_Name);
+	  break;
+	case PTP_OPC_AlbumArtist:
+	  if (alb->artist != NULL)
+	    free(alb->artist);
+	  alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_AlbumArtist);
+	  break;
+	case PTP_OPC_Artist:
+	  if (alb->artist == NULL)
+	    alb->artist = get_string_from_object(device, ob->oid, PTP_OPC_Artist);
+	  break;
+	case PTP_OPC_Composer:
+	  alb->composer = get_string_from_object(device, ob->oid, PTP_OPC_Composer);
+	  break;
+	case PTP_OPC_Genre:
+	  alb->genre = get_string_from_object(device, ob->oid, PTP_OPC_Genre);
+	  break;
+	default:
+	  break;
+	}
+      }
+      free(props);
+    }
+  }
+}
+
+/**
+ * This function returns a list of the albums available on the
+ * device.
+ *
+ * @param device a pointer to the device to get the album listing from.
+ * @return an album list on success, else NULL. If there are no albums
+ *         on the device, NULL will be returned as well.
+ * @see LIBMTP_Get_Album()
+ */
+LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *device)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  LIBMTP_album_t *retalbums = NULL;
+  LIBMTP_album_t *curalbum = NULL;
+  uint32_t i;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0)
+    flush_handles(device);
+
+  for (i = 0; i < params->nrofobjects; i++) {
+    LIBMTP_album_t *alb;
+    PTPObject *ob;
+    uint16_t ret;
+
+    ob = &params->objects[i];
+
+    // Ignore stuff that isn't an album
+    if ( ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum )
+      continue;
+
+    // Allocate a new album type
+    alb = LIBMTP_new_album_t();
+    alb->album_id = ob->oid;
+    alb->parent_id = ob->oi.ParentObject;
+    alb->storage_id = ob->oi.StorageID;
+
+    // Fetch supported metadata
+    get_album_metadata(device, alb);
+
+    // Then get the track listing for this album
+    ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
+    if (ret != PTP_RC_OK) {
+      add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album_List(): Could not get object references.");
+      alb->tracks = NULL;
+      alb->no_tracks = 0;
+    }
+
+    // Add album to a list that will be returned afterwards.
+    if (retalbums == NULL) {
+      retalbums = alb;
+      curalbum = alb;
+    } else {
+      curalbum->next = alb;
+      curalbum = alb;
+    }
+
+  }
+  return retalbums;
+}
+
+/**
+ * This function retrieves an individual album from the device.
+ * @param device a pointer to the device to get the album from.
+ * @param albid the unique ID of the album to retrieve.
+ * @return a valid album metadata or NULL on failure.
+ * @see LIBMTP_Get_Album_List()
+ */
+LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *device, uint32_t const albid)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t ret;
+  PTPObject *ob;
+  LIBMTP_album_t *alb;
+
+  // Get all the handles if we haven't already done that
+  if (params->nrofobjects == 0)
+    flush_handles(device);
+
+  ret = ptp_object_want(params, albid, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK)
+    return NULL;
+
+  // Ignore stuff that isn't an album
+  if (ob->oi.ObjectFormat != PTP_OFC_MTP_AbstractAudioAlbum)
+    return NULL;
+
+  // Allocate a new album type
+  alb = LIBMTP_new_album_t();
+  alb->album_id = ob->oid;
+  alb->parent_id = ob->oi.ParentObject;
+  alb->storage_id = ob->oi.StorageID;
+
+  // Fetch supported metadata
+  get_album_metadata(device, alb);
+
+  // Then get the track listing for this album
+  ret = ptp_mtp_getobjectreferences(params, alb->album_id, &alb->tracks, &alb->no_tracks);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Album: Could not get object references.");
+    alb->tracks = NULL;
+    alb->no_tracks = 0;
+  }
+
+  return alb;
+}
+
+/**
+ * This routine creates a new album based on the metadata
+ * supplied. If the <code>tracks</code> field of the metadata
+ * contains a track listing, these tracks will be added to the
+ * album.
+ * @param device a pointer to the device to create the new album on.
+ * @param metadata the metadata for the new album. If the function
+ *        exits with success, the <code>album_id</code> field of this
+ *        struct will contain the new ID of the album.
+ *        <ul>
+ *        <li><code>metadata-&gt;parent_id</code> should be set to the parent
+ *        (e.g. folder) to store this track in. Since some
+ *        devices are a bit picky about where files
+ *        are placed, a default folder will be chosen if libmtp
+ *        has detected one for the current filetype and this
+ *        parameter is set to 0. If this is 0 and no default folder
+ *        can be found, the file will be stored in the root folder.
+ *        <li><code>metadata-&gt;storage_id</code> should be set to the
+ *        desired storage (e.g. memory card or whatever your device
+ *        presents) to store this track in. Setting this to 0 will store
+ *        the track on the primary storage.
+ *        </ul>
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Update_Album()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *device,
+			    LIBMTP_album_t * const metadata)
+{
+  uint32_t localph = metadata->parent_id;
+
+  // Use a default folder if none given
+  if (localph == 0) {
+    if (device->default_album_folder != 0)
+      localph = device->default_album_folder;
+    else
+      localph = device->default_music_folder;
+  }
+  metadata->parent_id = localph;
+
+  // Just create a new abstract album...
+  return create_new_abstract_list(device,
+				  metadata->name,
+				  metadata->artist,
+				  metadata->composer,
+				  metadata->genre,
+				  localph,
+				  metadata->storage_id,
+				  PTP_OFC_MTP_AbstractAudioAlbum,
+				  ".alb",
+				  &metadata->album_id,
+				  metadata->tracks,
+				  metadata->no_tracks);
+}
+
+/**
+ * This creates a new sample data metadata structure and allocates memory
+ * for it. Notice that if you add strings to this structure they
+ * will be freed by the corresponding <code>LIBMTP_destroy_sampledata_t</code>
+ * operation later, so be careful of using strdup() when assigning
+ * strings.
+ *
+ * @return a pointer to the newly allocated metadata structure.
+ * @see LIBMTP_destroy_sampledata_t()
+ */
+LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void)
+{
+  LIBMTP_filesampledata_t *new = (LIBMTP_filesampledata_t *) malloc(sizeof(LIBMTP_filesampledata_t));
+  if (new == NULL) {
+    return NULL;
+  }
+  new->height=0;
+  new->width = 0;
+  new->data = NULL;
+  new->duration = 0;
+  new->size = 0;
+  return new;
+}
+
+/**
+ * This destroys a file sample metadata type.
+ * @param sample the file sample metadata to be destroyed.
+ */
+void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t * sample)
+{
+  if (sample == NULL) {
+    return;
+  }
+  if (sample->data != NULL) {
+    free(sample->data);
+  }
+  free(sample);
+}
+
+/**
+ * This routine figures out whether a certain filetype supports
+ * representative samples (small thumbnail images) or not. This
+ * typically applies to JPEG files, MP3 files and Album abstract
+ * playlists, but in theory any filetype could support representative
+ * samples.
+ * @param device a pointer to the device which is to be examined.
+ * @param filetype the fileype to examine, and return the representative sample
+ *        properties for.
+ * @param sample this will contain a new sample type with the fields
+ *        filled in with suitable default values. For example, the
+ *        supported sample type will be set, the supported height and
+ *        width will be set to max values if it is an image sample,
+ *        and duration will also be given some suitable default value
+ *        which should not be exceeded on audio samples. If the 
+ *        device does not support samples for this filetype, this
+ *        pointer will be NULL. If it is not NULL, the user must
+ *        destroy this struct with <code>LIBMTP_destroy_filesampledata_t()</code>
+ *        after use.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Send_Representative_Sample()
+ * @see LIBMTP_Create_New_Album()
+ */
+int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *device,
+					    LIBMTP_filetype_t const filetype,
+					    LIBMTP_filesampledata_t ** sample)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  uint16_t *props = NULL;
+  uint32_t propcnt = 0;
+  int i;
+  // TODO: Get rid of these when we can properly query the device.
+  int support_data = 0;
+  int support_format = 0;
+  int support_height = 0;
+  int support_width = 0;
+  int support_duration = 0;
+  int support_size = 0;
+
+  PTPObjectPropDesc opd_height;
+  PTPObjectPropDesc opd_width;
+  PTPObjectPropDesc opd_format;
+  PTPObjectPropDesc opd_duration;
+  PTPObjectPropDesc opd_size;
+  
+  // Default to no type supported.
+  *sample = NULL;
+  
+  ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(filetype), &propcnt, &props);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample_Format(): could not get object properties.");
+    return -1;
+  }
+  /*
+   * TODO: when walking through these object properties, make calls to
+   * a new function in ptp.h/ptp.c that can send the command 
+   * PTP_OC_MTP_GetObjectPropDesc to get max/min values of the properties
+   * supported.
+   */
+  for (i = 0; i < propcnt; i++) {
+    switch(props[i]) {
+    case PTP_OPC_RepresentativeSampleData:
+      support_data = 1;
+      break;
+    case PTP_OPC_RepresentativeSampleFormat:
+      support_format = 1;
+      break;
+    case PTP_OPC_RepresentativeSampleSize:
+      support_size = 1;
+      break;
+    case PTP_OPC_RepresentativeSampleHeight:
+      support_height = 1;
+      break;
+    case PTP_OPC_RepresentativeSampleWidth:
+      support_width = 1;
+      break;
+    case PTP_OPC_RepresentativeSampleDuration:
+      support_duration = 1;
+      break;
+    default:
+      break;
+    }
+  }
+  free(props);
+    
+  if (support_data && support_format && support_height && support_width && !support_duration) {
+    // Something that supports height and width and not duration is likely to be JPEG
+    LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
+    /* 
+     * Populate the sample format with the first supported format 
+     * 
+     * TODO: figure out how to pass back more than one format if more are
+     * supported by the device.
+     */
+    ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);    
+    retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
+    ptp_free_objectpropdesc(&opd_format);
+    /* Populate the maximum image height */
+    ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleWidth, map_libmtp_type_to_ptp_type(filetype), &opd_width);        
+    retsam->width = opd_width.FORM.Range.MaximumValue.u32;
+    ptp_free_objectpropdesc(&opd_width);
+    /* Populate the maximum image width */
+    ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleHeight, map_libmtp_type_to_ptp_type(filetype), &opd_height);    						
+    retsam->height = opd_height.FORM.Range.MaximumValue.u32;
+    ptp_free_objectpropdesc(&opd_height);
+    /* Populate the maximum size */
+    if (support_size) {
+      ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
+      retsam->size = opd_size.FORM.Range.MaximumValue.u32;
+      ptp_free_objectpropdesc(&opd_size);
+    }
+    *sample = retsam;
+  } else if (support_data && support_format && !support_height && !support_width && support_duration) {
+    // Another qualified guess
+    LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t();
+    /* 
+     * Populate the sample format with the first supported format 
+     * 
+     * TODO: figure out how to pass back more than one format if more are
+     * supported by the device.
+     */
+    ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format);    
+    retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16);
+    ptp_free_objectpropdesc(&opd_format);
+    /* Populate the maximum duration */
+    ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleDuration, map_libmtp_type_to_ptp_type(filetype), &opd_duration);    
+    retsam->duration = opd_duration.FORM.Range.MaximumValue.u32;
+    ptp_free_objectpropdesc(&opd_duration);
+    /* Populate the maximum size */
+    if (support_size) {
+      ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleSize, map_libmtp_type_to_ptp_type(filetype), &opd_size);
+      retsam->size = opd_size.FORM.Range.MaximumValue.u32;
+      ptp_free_objectpropdesc(&opd_size);
+    }
+    *sample = retsam;
+  }
+  return 0;
+}
+
+/**
+ * This routine sends representative sample data for an object.
+ * This uses the RepresentativeSampleData property of the album,
+ * if the device supports it. The data should be of a format acceptable
+ * to the player (for iRiver and Creative, this seems to be JPEG) and
+ * must not be too large. (for a Creative, max seems to be about 20KB.)
+ * Check by calling LIBMTP_Get_Representative_Sample_Format() to get
+ * maximum size, dimensions, etc..
+ * @param device a pointer to the device which the object is on.
+ * @param id unique id of the object to set artwork for.
+ * @param pointer to LIBMTP_filesampledata_t struct containing data
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Get_Representative_Sample()
+ * @see LIBMTP_Get_Representative_Sample_Format()
+ * @see LIBMTP_Create_New_Album()
+ */
+int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *device,
+                          uint32_t const id,
+                          LIBMTP_filesampledata_t *sampledata)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  PTPPropertyValue propval;
+  PTPObject *ob;
+  uint32_t i;
+  uint16_t *props = NULL;
+  uint32_t propcnt = 0;
+  int supported = 0;
+
+  // get the file format for the object we're going to send representative data for
+  ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): could not get object info.");
+    return -1;
+  }
+
+  // check that we can send representative sample data for this object format
+  ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not get object properties.");
+    return -1;
+  }
+
+  for (i = 0; i < propcnt; i++) {
+    if (props[i] == PTP_OPC_RepresentativeSampleData) {
+      supported = 1;
+      break;
+    }
+  }
+  if (!supported) {
+    free(props);
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
+    return -1;
+  }
+  free(props);
+  
+  // Go ahead and send the data
+  propval.a.count = sampledata->size;
+  propval.a.v = malloc(sizeof(PTPPropertyValue) * sampledata->size);
+  for (i = 0; i < sampledata->size; i++) {
+    propval.a.v[i].u8 = sampledata->data[i];
+  }
+  
+  ret = ptp_mtp_setobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
+				   &propval,PTP_DTC_AUINT8);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_Representative_Sample(): could not send sample data.");
+    free(propval.a.v);
+    return -1;
+  }
+  free(propval.a.v);
+  
+  /* Set the height and width if the sample is an image, otherwise just
+   * set the duration and size */
+  switch(sampledata->filetype) {
+  case LIBMTP_FILETYPE_JPEG:
+  case LIBMTP_FILETYPE_JFIF:
+  case LIBMTP_FILETYPE_TIFF:
+  case LIBMTP_FILETYPE_BMP:
+  case LIBMTP_FILETYPE_GIF:
+  case LIBMTP_FILETYPE_PICT:
+  case LIBMTP_FILETYPE_PNG:
+    if (!FLAG_BROKEN_SET_SAMPLE_DIMENSIONS(ptp_usb)) {
+      // For images, set the height and width
+      set_object_u32(device, id, PTP_OPC_RepresentativeSampleHeight, sampledata->height);
+      set_object_u32(device, id, PTP_OPC_RepresentativeSampleWidth, sampledata->width);		
+    }
+    break;
+  default:
+    // For anything not an image, set the duration and size
+    set_object_u32(device, id, PTP_OPC_RepresentativeSampleDuration, sampledata->duration);
+    set_object_u32(device, id, PTP_OPC_RepresentativeSampleSize, sampledata->size);
+    break;  		
+  }
+    
+  return 0;
+}
+
+/**
+ * This routine gets representative sample data for an object.
+ * This uses the RepresentativeSampleData property of the album,
+ * if the device supports it.
+ * @param device a pointer to the device which the object is on.
+ * @param id unique id of the object to get data for.
+ * @param pointer to LIBMTP_filesampledata_t struct to receive data
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Send_Representative_Sample()
+ * @see LIBMTP_Get_Representative_Sample_Format()
+ * @see LIBMTP_Create_New_Album()
+ */
+int LIBMTP_Get_Representative_Sample(LIBMTP_mtpdevice_t *device,
+                          uint32_t const id,
+                          LIBMTP_filesampledata_t *sampledata)
+{
+  uint16_t ret;
+  PTPParams *params = (PTPParams *) device->params;
+  PTPPropertyValue propval;
+  PTPObject *ob;
+  uint32_t i;
+  uint16_t *props = NULL;
+  uint32_t propcnt = 0;
+  int supported = 0;
+
+  // get the file format for the object we're going to send representative data for
+  ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
+  if (ret != PTP_RC_OK) {
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): could not get object info.");
+    return -1;
+  }
+
+  // check that we can store representative sample data for this object format
+  ret = ptp_mtp_getobjectpropssupported(params, ob->oi.ObjectFormat, &propcnt, &props);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get object properties.");
+    return -1;
+  }
+
+  for (i = 0; i < propcnt; i++) {
+    if (props[i] == PTP_OPC_RepresentativeSampleData) {
+      supported = 1;
+      break;
+    }
+  }
+  if (!supported) {
+    free(props);
+    add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_Representative_Sample(): object type doesn't support RepresentativeSampleData.");
+    return -1;
+  }
+  free(props);
+  
+  // Get the data
+  ret = ptp_mtp_getobjectpropvalue(params,id,PTP_OPC_RepresentativeSampleData,
+				   &propval,PTP_DTC_AUINT8);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Representative_Sample(): could not get sample data.");
+    return -1;
+  }
+  
+  // Store it
+  sampledata->size = propval.a.count;
+  sampledata->data = malloc(sizeof(PTPPropertyValue) * propval.a.count);
+  for (i = 0; i < propval.a.count; i++) {
+    sampledata->data[i] = propval.a.v[i].u8;
+  }
+  free(propval.a.v);
+
+  // Get the other properties  
+  sampledata->width = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleWidth, 0);
+  sampledata->height = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleHeight, 0);
+  sampledata->duration = get_u32_from_object(device, id, PTP_OPC_RepresentativeSampleDuration, 0);
+  sampledata->filetype = map_ptp_type_to_libmtp_type(
+        get_u16_from_object(device, id, PTP_OPC_RepresentativeSampleFormat, LIBMTP_FILETYPE_UNKNOWN));
+    
+  return 0;
+}
+
+/**
+ * This routine updates an album based on the metadata
+ * supplied. If the <code>tracks</code> field of the metadata
+ * contains a track listing, these tracks will be added to the
+ * album in place of those already present, i.e. the
+ * previous track listing will be deleted.
+ * @param device a pointer to the device to create the new album on.
+ * @param metadata the metadata for the album to be updated.
+ *                 notice that the field <code>album_id</code>
+ *                 must contain the apropriate album ID.
+ * @return 0 on success, any other value means failure.
+ * @see LIBMTP_Create_New_Album()
+ * @see LIBMTP_Delete_Object()
+ */
+int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device,
+			   LIBMTP_album_t const * const metadata)
+{
+  return update_abstract_list(device,
+			      metadata->name,
+			      metadata->artist,
+			      metadata->composer,
+			      metadata->genre,
+			      metadata->album_id,
+			      PTP_OFC_MTP_AbstractAudioAlbum,
+			      metadata->tracks,
+			      metadata->no_tracks);
+}
+
+/**
+ * Dummy function needed to interface to upstream
+ * ptp.c/ptp.h files.
+ */
+void ptp_nikon_getptpipguid (unsigned char* guid) {
+  return;
+}
+
+/**
+ * Add an object to cache.
+ * @param device the device which may have a cache to which the object should be added.
+ * @param object_id the object to add to the cache.
+ */ 
+static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
+{
+  PTPParams *params = (PTPParams *)device->params;
+  uint16_t ret;
+
+  ret = ptp_add_object_to_cache(params, object_id);
+  if (ret != PTP_RC_OK) {
+    add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): couldn't add object to cache");
+  }
+}
+
+
+/**
+ * Update cache after object has been modified
+ * @param device the device which may have a cache to which the object should be updated.
+ * @param object_id the object to update.
+ */
+static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t object_id)
+{
+  PTPParams *params = (PTPParams *)device->params;
+
+  ptp_remove_object_from_cache(params, object_id);
+  add_object_to_cache(device, object_id);
+}
diff --git a/src/libmtp.h b/src/libmtp.h
new file mode 100644
index 0000000..7657ad3
--- /dev/null
+++ b/src/libmtp.h
@@ -0,0 +1,940 @@
+/**
+ * \file libmtp.h
+ * Interface to the Media Transfer Protocol library.
+ *
+ * Copyright (C) 2005-2008 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
+ * Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * <code>
+ * #include <libmtp.h>
+ * </code>
+ */
+#ifndef LIBMTP_H_INCLUSION_GUARD
+#define LIBMTP_H_INCLUSION_GUARD
+
+#define LIBMTP_VERSION 1.0.1
+#define LIBMTP_VERSION_STRING "1.0.1"
+
+/* This handles MSVC pecularities */
+#ifdef _MSC_VER
+#include <windows.h>
+#define __WIN32__
+#define snprintf _snprintf
+#define ssize_t SSIZE_T
+/*
+ * Types that do not exist in Windows
+ * sys/types.h, but they exist in mingw32
+ * sys/types.h.
+ */
+typedef char int8_t;
+typedef unsigned char uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+
+#include <stdio.h>
+#include <usb.h>
+#include <stdint.h>
+
+/**
+ * @defgroup types libmtp global type definitions
+ * @{
+ * The filetypes defined here are the external types used
+ * by the libmtp library interface. The types used internally
+ * as PTP-defined enumerator types is something different.
+ */
+typedef enum {
+  LIBMTP_FILETYPE_FOLDER,
+  LIBMTP_FILETYPE_WAV,
+  LIBMTP_FILETYPE_MP3,
+  LIBMTP_FILETYPE_WMA,
+  LIBMTP_FILETYPE_OGG,
+  LIBMTP_FILETYPE_AUDIBLE,
+  LIBMTP_FILETYPE_MP4,
+  LIBMTP_FILETYPE_UNDEF_AUDIO,
+  LIBMTP_FILETYPE_WMV,
+  LIBMTP_FILETYPE_AVI,
+  LIBMTP_FILETYPE_MPEG,
+  LIBMTP_FILETYPE_ASF,
+  LIBMTP_FILETYPE_QT,
+  LIBMTP_FILETYPE_UNDEF_VIDEO,
+  LIBMTP_FILETYPE_JPEG,
+  LIBMTP_FILETYPE_JFIF,
+  LIBMTP_FILETYPE_TIFF,
+  LIBMTP_FILETYPE_BMP,
+  LIBMTP_FILETYPE_GIF,
+  LIBMTP_FILETYPE_PICT,
+  LIBMTP_FILETYPE_PNG,
+  LIBMTP_FILETYPE_VCALENDAR1,
+  LIBMTP_FILETYPE_VCALENDAR2,
+  LIBMTP_FILETYPE_VCARD2,
+  LIBMTP_FILETYPE_VCARD3,
+  LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT,
+  LIBMTP_FILETYPE_WINEXEC,
+  LIBMTP_FILETYPE_TEXT,
+  LIBMTP_FILETYPE_HTML,
+  LIBMTP_FILETYPE_FIRMWARE,
+  LIBMTP_FILETYPE_AAC,
+  LIBMTP_FILETYPE_MEDIACARD,
+  LIBMTP_FILETYPE_FLAC,
+  LIBMTP_FILETYPE_MP2,
+  LIBMTP_FILETYPE_M4A,
+  LIBMTP_FILETYPE_DOC,
+  LIBMTP_FILETYPE_XML,
+  LIBMTP_FILETYPE_XLS,
+  LIBMTP_FILETYPE_PPT,
+  LIBMTP_FILETYPE_MHT,
+  LIBMTP_FILETYPE_JP2,
+  LIBMTP_FILETYPE_JPX,
+  LIBMTP_FILETYPE_ALBUM,
+  LIBMTP_FILETYPE_PLAYLIST,
+  LIBMTP_FILETYPE_UNKNOWN
+} LIBMTP_filetype_t;
+
+/**
+ * \def LIBMTP_FILETYPE_IS_AUDIO
+ * Audio filetype test.
+ *
+ * For filetypes that can be either audio
+ * or video, use LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ */
+#define LIBMTP_FILETYPE_IS_AUDIO(a)\
+(a == LIBMTP_FILETYPE_WAV ||\
+ a == LIBMTP_FILETYPE_MP3 ||\
+ a == LIBMTP_FILETYPE_MP2 ||\
+ a == LIBMTP_FILETYPE_WMA ||\
+ a == LIBMTP_FILETYPE_OGG ||\
+ a == LIBMTP_FILETYPE_FLAC ||\
+ a == LIBMTP_FILETYPE_AAC ||\
+ a == LIBMTP_FILETYPE_M4A ||\
+ a == LIBMTP_FILETYPE_AUDIBLE ||\
+ a == LIBMTP_FILETYPE_UNDEF_AUDIO)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_VIDEO
+ *  Video filetype test.
+ *
+ * For filetypes that can be either audio
+ * or video, use LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ */
+#define LIBMTP_FILETYPE_IS_VIDEO(a)\
+(a == LIBMTP_FILETYPE_WMV ||\
+ a == LIBMTP_FILETYPE_AVI ||\
+ a == LIBMTP_FILETYPE_MPEG ||\
+ a == LIBMTP_FILETYPE_UNDEF_VIDEO)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ *  Audio and&slash;or video filetype test.
+ */
+#define LIBMTP_FILETYPE_IS_AUDIOVIDEO(a)\
+(a == LIBMTP_FILETYPE_MP4 ||\
+ a == LIBMTP_FILETYPE_ASF ||\
+ a == LIBMTP_FILETYPE_QT)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_TRACK
+ *  Test if filetype is a track.
+ *  Use this to determine if the File API or Track API
+ *  should be used to upload or download an object.
+ */
+#define LIBMTP_FILETYPE_IS_TRACK(a)\
+(LIBMTP_FILETYPE_IS_AUDIO(a) ||\
+ LIBMTP_FILETYPE_IS_VIDEO(a) ||\
+ LIBMTP_FILETYPE_IS_AUDIOVIDEO(a))
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_IMAGE
+ *  Image filetype test
+ */
+#define LIBMTP_FILETYPE_IS_IMAGE(a)\
+(a == LIBMTP_FILETYPE_JPEG ||\
+a == LIBMTP_FILETYPE_JFIF ||\
+a == LIBMTP_FILETYPE_TIFF ||\
+a == LIBMTP_FILETYPE_BMP ||\
+a == LIBMTP_FILETYPE_GIF ||\
+a == LIBMTP_FILETYPE_PICT ||\
+a == LIBMTP_FILETYPE_PNG ||\
+a == LIBMTP_FILETYPE_JP2 ||\
+a == LIBMTP_FILETYPE_JPX ||\
+a == LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_ADDRESSBOOK
+ *  Addressbook and Business card filetype test
+ */
+#define LIBMTP_FILETYPE_IS_ADDRESSBOOK(a)\
+(a == LIBMTP_FILETYPE_VCARD2 ||\
+a == LIBMTP_FILETYPE_VCARD2)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_CALENDAR
+ *  Calendar and Appointment filetype test
+ */
+#define LIBMTP_FILETYPE_IS_CALENDAR(a)\
+(a == LIBMTP_FILETYPE_VCALENDAR1 ||\
+a == LIBMTP_FILETYPE_VCALENDAR2)
+
+/**
+ * The properties defined here are the external types used
+ * by the libmtp library interface.
+ */
+typedef enum {
+  LIBMTP_PROPERTY_StorageID,
+  LIBMTP_PROPERTY_ObjectFormat,
+  LIBMTP_PROPERTY_ProtectionStatus,
+  LIBMTP_PROPERTY_ObjectSize,
+  LIBMTP_PROPERTY_AssociationType,
+  LIBMTP_PROPERTY_AssociationDesc,
+  LIBMTP_PROPERTY_ObjectFileName,
+  LIBMTP_PROPERTY_DateCreated,
+  LIBMTP_PROPERTY_DateModified,
+  LIBMTP_PROPERTY_Keywords,
+  LIBMTP_PROPERTY_ParentObject,
+  LIBMTP_PROPERTY_AllowedFolderContents,
+  LIBMTP_PROPERTY_Hidden,
+  LIBMTP_PROPERTY_SystemObject,
+  LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier,
+  LIBMTP_PROPERTY_SyncID,
+  LIBMTP_PROPERTY_PropertyBag,
+  LIBMTP_PROPERTY_Name,
+  LIBMTP_PROPERTY_CreatedBy,
+  LIBMTP_PROPERTY_Artist,
+  LIBMTP_PROPERTY_DateAuthored,
+  LIBMTP_PROPERTY_Description,
+  LIBMTP_PROPERTY_URLReference,
+  LIBMTP_PROPERTY_LanguageLocale,
+  LIBMTP_PROPERTY_CopyrightInformation,
+  LIBMTP_PROPERTY_Source,
+  LIBMTP_PROPERTY_OriginLocation,
+  LIBMTP_PROPERTY_DateAdded,
+  LIBMTP_PROPERTY_NonConsumable,
+  LIBMTP_PROPERTY_CorruptOrUnplayable,
+  LIBMTP_PROPERTY_ProducerSerialNumber,
+  LIBMTP_PROPERTY_RepresentativeSampleFormat,
+  LIBMTP_PROPERTY_RepresentativeSampleSize,
+  LIBMTP_PROPERTY_RepresentativeSampleHeight,
+  LIBMTP_PROPERTY_RepresentativeSampleWidth,
+  LIBMTP_PROPERTY_RepresentativeSampleDuration,
+  LIBMTP_PROPERTY_RepresentativeSampleData,
+  LIBMTP_PROPERTY_Width,
+  LIBMTP_PROPERTY_Height,
+  LIBMTP_PROPERTY_Duration,
+  LIBMTP_PROPERTY_Rating,
+  LIBMTP_PROPERTY_Track,
+  LIBMTP_PROPERTY_Genre,
+  LIBMTP_PROPERTY_Credits,
+  LIBMTP_PROPERTY_Lyrics,
+  LIBMTP_PROPERTY_SubscriptionContentID,
+  LIBMTP_PROPERTY_ProducedBy,
+  LIBMTP_PROPERTY_UseCount,
+  LIBMTP_PROPERTY_SkipCount,
+  LIBMTP_PROPERTY_LastAccessed,
+  LIBMTP_PROPERTY_ParentalRating,
+  LIBMTP_PROPERTY_MetaGenre,
+  LIBMTP_PROPERTY_Composer,
+  LIBMTP_PROPERTY_EffectiveRating,
+  LIBMTP_PROPERTY_Subtitle,
+  LIBMTP_PROPERTY_OriginalReleaseDate,
+  LIBMTP_PROPERTY_AlbumName,
+  LIBMTP_PROPERTY_AlbumArtist,
+  LIBMTP_PROPERTY_Mood,
+  LIBMTP_PROPERTY_DRMStatus,
+  LIBMTP_PROPERTY_SubDescription,
+  LIBMTP_PROPERTY_IsCropped,
+  LIBMTP_PROPERTY_IsColorCorrected,
+  LIBMTP_PROPERTY_ImageBitDepth,
+  LIBMTP_PROPERTY_Fnumber,
+  LIBMTP_PROPERTY_ExposureTime,
+  LIBMTP_PROPERTY_ExposureIndex,
+  LIBMTP_PROPERTY_DisplayName,
+  LIBMTP_PROPERTY_BodyText,
+  LIBMTP_PROPERTY_Subject,
+  LIBMTP_PROPERTY_Priority,
+  LIBMTP_PROPERTY_GivenName,
+  LIBMTP_PROPERTY_MiddleNames,
+  LIBMTP_PROPERTY_FamilyName,
+  LIBMTP_PROPERTY_Prefix,
+  LIBMTP_PROPERTY_Suffix,
+  LIBMTP_PROPERTY_PhoneticGivenName,
+  LIBMTP_PROPERTY_PhoneticFamilyName,
+  LIBMTP_PROPERTY_EmailPrimary,
+  LIBMTP_PROPERTY_EmailPersonal1,
+  LIBMTP_PROPERTY_EmailPersonal2,
+  LIBMTP_PROPERTY_EmailBusiness1,
+  LIBMTP_PROPERTY_EmailBusiness2,
+  LIBMTP_PROPERTY_EmailOthers,
+  LIBMTP_PROPERTY_PhoneNumberPrimary,
+  LIBMTP_PROPERTY_PhoneNumberPersonal,
+  LIBMTP_PROPERTY_PhoneNumberPersonal2,
+  LIBMTP_PROPERTY_PhoneNumberBusiness,
+  LIBMTP_PROPERTY_PhoneNumberBusiness2,
+  LIBMTP_PROPERTY_PhoneNumberMobile,
+  LIBMTP_PROPERTY_PhoneNumberMobile2,
+  LIBMTP_PROPERTY_FaxNumberPrimary,
+  LIBMTP_PROPERTY_FaxNumberPersonal,
+  LIBMTP_PROPERTY_FaxNumberBusiness,
+  LIBMTP_PROPERTY_PagerNumber,
+  LIBMTP_PROPERTY_PhoneNumberOthers,
+  LIBMTP_PROPERTY_PrimaryWebAddress,
+  LIBMTP_PROPERTY_PersonalWebAddress,
+  LIBMTP_PROPERTY_BusinessWebAddress,
+  LIBMTP_PROPERTY_InstantMessengerAddress,
+  LIBMTP_PROPERTY_InstantMessengerAddress2,
+  LIBMTP_PROPERTY_InstantMessengerAddress3,
+  LIBMTP_PROPERTY_PostalAddressPersonalFull,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullLine1,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullLine2,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullCity,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullRegion,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullCountry,
+  LIBMTP_PROPERTY_PostalAddressBusinessFull,
+  LIBMTP_PROPERTY_PostalAddressBusinessLine1,
+  LIBMTP_PROPERTY_PostalAddressBusinessLine2,
+  LIBMTP_PROPERTY_PostalAddressBusinessCity,
+  LIBMTP_PROPERTY_PostalAddressBusinessRegion,
+  LIBMTP_PROPERTY_PostalAddressBusinessPostalCode,
+  LIBMTP_PROPERTY_PostalAddressBusinessCountry,
+  LIBMTP_PROPERTY_PostalAddressOtherFull,
+  LIBMTP_PROPERTY_PostalAddressOtherLine1,
+  LIBMTP_PROPERTY_PostalAddressOtherLine2,
+  LIBMTP_PROPERTY_PostalAddressOtherCity,
+  LIBMTP_PROPERTY_PostalAddressOtherRegion,
+  LIBMTP_PROPERTY_PostalAddressOtherPostalCode,
+  LIBMTP_PROPERTY_PostalAddressOtherCountry,
+  LIBMTP_PROPERTY_OrganizationName,
+  LIBMTP_PROPERTY_PhoneticOrganizationName,
+  LIBMTP_PROPERTY_Role,
+  LIBMTP_PROPERTY_Birthdate,
+  LIBMTP_PROPERTY_MessageTo,
+  LIBMTP_PROPERTY_MessageCC,
+  LIBMTP_PROPERTY_MessageBCC,
+  LIBMTP_PROPERTY_MessageRead,
+  LIBMTP_PROPERTY_MessageReceivedTime,
+  LIBMTP_PROPERTY_MessageSender,
+  LIBMTP_PROPERTY_ActivityBeginTime,
+  LIBMTP_PROPERTY_ActivityEndTime,
+  LIBMTP_PROPERTY_ActivityLocation,
+  LIBMTP_PROPERTY_ActivityRequiredAttendees,
+  LIBMTP_PROPERTY_ActivityOptionalAttendees,
+  LIBMTP_PROPERTY_ActivityResources,
+  LIBMTP_PROPERTY_ActivityAccepted,
+  LIBMTP_PROPERTY_Owner,
+  LIBMTP_PROPERTY_Editor,
+  LIBMTP_PROPERTY_Webmaster,
+  LIBMTP_PROPERTY_URLSource,
+  LIBMTP_PROPERTY_URLDestination,
+  LIBMTP_PROPERTY_TimeBookmark,
+  LIBMTP_PROPERTY_ObjectBookmark,
+  LIBMTP_PROPERTY_ByteBookmark,
+  LIBMTP_PROPERTY_LastBuildDate,
+  LIBMTP_PROPERTY_TimetoLive,
+  LIBMTP_PROPERTY_MediaGUID,
+  LIBMTP_PROPERTY_TotalBitRate,
+  LIBMTP_PROPERTY_BitRateType,
+  LIBMTP_PROPERTY_SampleRate,
+  LIBMTP_PROPERTY_NumberOfChannels,
+  LIBMTP_PROPERTY_AudioBitDepth,
+  LIBMTP_PROPERTY_ScanDepth,
+  LIBMTP_PROPERTY_AudioWAVECodec,
+  LIBMTP_PROPERTY_AudioBitRate,
+  LIBMTP_PROPERTY_VideoFourCCCodec,
+  LIBMTP_PROPERTY_VideoBitRate,
+  LIBMTP_PROPERTY_FramesPerThousandSeconds,
+  LIBMTP_PROPERTY_KeyFrameDistance,
+  LIBMTP_PROPERTY_BufferSize,
+  LIBMTP_PROPERTY_EncodingQuality,
+  LIBMTP_PROPERTY_EncodingProfile,
+  LIBMTP_PROPERTY_BuyFlag,
+  LIBMTP_PROPERTY_UNKNOWN
+} LIBMTP_property_t;
+
+/**
+ * These are the data types
+ */
+typedef enum {
+  LIBMTP_DATATYPE_INT8,
+  LIBMTP_DATATYPE_UINT8,
+  LIBMTP_DATATYPE_INT16,
+  LIBMTP_DATATYPE_UINT16,
+  LIBMTP_DATATYPE_INT32,
+  LIBMTP_DATATYPE_UINT32,
+  LIBMTP_DATATYPE_INT64,
+  LIBMTP_DATATYPE_UINT64,
+} LIBMTP_datatype_t;
+
+/**
+ * These are the numbered error codes. You can also
+ * get string representations for errors.
+ */
+typedef enum {
+  LIBMTP_ERROR_NONE,
+  LIBMTP_ERROR_GENERAL,
+  LIBMTP_ERROR_PTP_LAYER,
+  LIBMTP_ERROR_USB_LAYER,
+  LIBMTP_ERROR_MEMORY_ALLOCATION,
+  LIBMTP_ERROR_NO_DEVICE_ATTACHED,
+  LIBMTP_ERROR_STORAGE_FULL,
+  LIBMTP_ERROR_CONNECTING,
+  LIBMTP_ERROR_CANCELLED
+} LIBMTP_error_number_t;
+typedef struct LIBMTP_device_entry_struct LIBMTP_device_entry_t; /**< @see LIBMTP_device_entry_struct */
+typedef struct LIBMTP_raw_device_struct LIBMTP_raw_device_t; /**< @see LIBMTP_raw_device_struct */
+typedef struct LIBMTP_error_struct LIBMTP_error_t; /**< @see LIBMTP_error_struct */
+typedef struct LIBMTP_allowed_values_struct LIBMTP_allowed_values_t; /**< @see LIBMTP_allowed_values_struct */
+typedef struct LIBMTP_mtpdevice_struct LIBMTP_mtpdevice_t; /**< @see LIBMTP_mtpdevice_struct */
+typedef struct LIBMTP_file_struct LIBMTP_file_t; /**< @see LIBMTP_file_struct */
+typedef struct LIBMTP_track_struct LIBMTP_track_t; /**< @see LIBMTP_track_struct */
+typedef struct LIBMTP_playlist_struct LIBMTP_playlist_t; /**< @see LIBMTP_playlist_struct */
+typedef struct LIBMTP_album_struct LIBMTP_album_t; /**< @see LIBMTP_album_struct */
+typedef struct LIBMTP_folder_struct LIBMTP_folder_t; /**< @see LIBMTP_folder_t */
+typedef struct LIBMTP_object_struct LIBMTP_object_t; /**< @see LIBMTP_object_t */
+typedef struct LIBMTP_filesampledata_struct LIBMTP_filesampledata_t; /**< @see LIBMTP_filesample_t */
+typedef struct LIBMTP_devicestorage_struct LIBMTP_devicestorage_t; /**< @see LIBMTP_devicestorage_t */
+
+/**
+ * The callback type definition. Notice that a progress percentage ratio
+ * is easy to calculate by dividing <code>sent</code> by
+ * <code>total</code>.
+ * @param sent the number of bytes sent so far
+ * @param total the total number of bytes to send
+ * @param data a user-defined dereferencable pointer
+ * @return if anything else than 0 is returned, the current transfer will be
+ *         interrupted / cancelled.
+ */
+typedef int (* LIBMTP_progressfunc_t) (uint64_t const sent, uint64_t const total,
+                		void const * const data);
+
+/**
+ * Callback function for get by handler function
+ * @param params the device parameters
+ * @param priv a user-defined dereferencable pointer
+ * @param wantlen the number of bytes wanted
+ * @param data a buffer to write the data to
+ * @param gotlen pointer to the number of bytes actually written
+ *        to data
+ * @return LIBMTP_HANDLER_RETURN_OK if successful,
+ *         LIBMTP_HANDLER_RETURN_ERROR on error or
+ *         LIBMTP_HANDLER_RETURN_CANCEL to cancel the transfer
+ */
+typedef uint16_t (* MTPDataGetFunc)	(void* params, void* priv,
+					uint32_t wantlen, unsigned char *data, uint32_t *gotlen);
+
+/**
+ * Callback function for put by handler function
+ * @param params the device parameters
+ * @param priv a user-defined dereferencable pointer
+ * @param sendlen the number of bytes available
+ * @param data a buffer to read the data from
+ * @param putlen pointer to the number of bytes actually read
+ *        from data
+ * @return LIBMTP_HANDLER_RETURN_OK if successful,
+ *         LIBMTP_HANDLER_RETURN_ERROR on error or
+ *         LIBMTP_HANDLER_RETURN_CANCEL to cancel the transfer
+ */
+typedef uint16_t (* MTPDataPutFunc)	(void* params, void* priv,
+					uint32_t sendlen, unsigned char *data, uint32_t *putlen);
+
+/**
+ * The return codes for the get/put functions
+ */
+#define LIBMTP_HANDLER_RETURN_OK 0
+#define LIBMTP_HANDLER_RETURN_ERROR 1
+#define LIBMTP_HANDLER_RETURN_CANCEL 2
+
+/**
+ * @}
+ * @defgroup structar libmtp data structures
+ * @{
+ */
+
+/**
+ * A data structure to hold MTP device entries.
+ */
+struct LIBMTP_device_entry_struct {
+  char *vendor; /**< The vendor of this device */
+  uint16_t vendor_id; /**< Vendor ID for this device */
+  char *product; /**< The product name of this device */
+  uint16_t product_id; /**< Product ID for this device */
+  uint32_t device_flags; /**< Bugs, device specifics etc */
+};
+
+/**
+ * A data structure to hold a raw MTP device connected
+ * to the bus.
+ */
+struct LIBMTP_raw_device_struct {
+  LIBMTP_device_entry_t device_entry; /**< The device entry for this raw device */
+  uint32_t bus_location; /**< Location of the bus, if device available */
+  uint8_t devnum; /**< Device number on the bus, if device available */
+};
+
+/**
+ * A data structure to hold errors from the library.
+ */
+struct LIBMTP_error_struct {
+  LIBMTP_error_number_t errornumber;
+  char *error_text;
+  LIBMTP_error_t *next;
+};
+
+/**
+ * A data structure to hold allowed ranges of values
+ */
+struct LIBMTP_allowed_values_struct {
+  uint8_t   u8max;
+  uint8_t   u8min;
+  uint8_t   u8step;
+  uint8_t*  u8vals;
+  int8_t    i8max;
+  int8_t    i8min;
+  int8_t    i8step;
+  int8_t*   i8vals;
+  uint16_t  u16max;
+  uint16_t  u16min;
+  uint16_t  u16step;
+  uint16_t* u16vals;
+  int16_t   i16max;
+  int16_t   i16min;
+  int16_t   i16step;
+  int16_t*  i16vals;
+  uint32_t  u32max;
+  uint32_t  u32min;
+  uint32_t  u32step;
+  uint32_t* u32vals;
+  int32_t   i32max;
+  int32_t   i32min;
+  int32_t   i32step;
+  int32_t*  i32vals;
+  uint64_t  u64max;
+  uint64_t  u64min;
+  uint64_t  u64step;
+  uint64_t* u64vals;
+  int64_t   i64max;
+  int64_t   i64min;
+  int64_t   i64step;
+  int64_t*  i64vals;
+  /**
+   * Number of entries in the vals array
+   */
+  uint16_t  num_entries;
+  /**
+   * The datatype specifying which of the above is used
+  */
+  LIBMTP_datatype_t datatype;
+  /**
+   * Non zero for range, 0 for enum
+  */
+  int is_range;
+};
+
+/**
+ * Main MTP device object struct
+ */
+struct LIBMTP_mtpdevice_struct {
+  /**
+   * Object bitsize, typically 32 or 64.
+   */
+  uint8_t object_bitsize;
+  /**
+   * Parameters for this device, must be cast into
+   * \c (PTPParams*) before internal use.
+   */
+  void *params;
+  /**
+   * USB device for this device, must be cast into
+   * \c (PTP_USB*) before internal use.
+   */
+  void *usbinfo;
+  /** 
+   * The storage for this device, do not use strings in here without 
+   * copying them first, and beware that this list may be rebuilt at
+   * any time.
+   * @see LIBMTP_Get_Storage()
+   */
+  LIBMTP_devicestorage_t *storage;
+  /**
+   * The error stack. This shall be handled using the error getting
+   * and clearing functions, not by dereferencing this list.
+   */
+  LIBMTP_error_t *errorstack;
+  /** The maximum battery level for this device */
+  uint8_t maximum_battery_level;
+  /** Default music folder */
+  uint32_t default_music_folder;
+  /** Default playlist folder */
+  uint32_t default_playlist_folder;
+  /** Default picture folder */
+  uint32_t default_picture_folder;
+  /** Default video folder */
+  uint32_t default_video_folder;
+  /** Default organizer folder */
+  uint32_t default_organizer_folder;
+  /** Default ZENcast folder (only Creative devices...) */
+  uint32_t default_zencast_folder;
+  /** Default Album folder */
+  uint32_t default_album_folder;
+  /** Default Text folder */
+  uint32_t default_text_folder;
+  /** Per device iconv() converters, only used internally */
+  void *cd;
+  
+  /** Pointer to next device in linked list; NULL if this is the last device */
+  LIBMTP_mtpdevice_t *next;
+};
+
+/**
+ * MTP file struct
+ */
+struct LIBMTP_file_struct {
+  uint32_t item_id; /**< Unique item ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this file */
+  char *filename; /**< Filename of this file */
+  uint64_t filesize; /**< Size of file in bytes */
+  time_t modificationdate; /**< Date of last alteration of the file */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the current file */
+  LIBMTP_file_t *next; /**< Next file in list or NULL if last file */
+};
+
+/**
+ * MTP track struct
+ */
+struct LIBMTP_track_struct {
+  uint32_t item_id; /**< Unique item ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this track */
+  char *title; /**< Track title */
+  char *artist; /**< Name of recording artist */
+  char *composer; /**< Name of recording composer */
+  char *genre; /**< Genre name for track */
+  char *album; /**< Album name for track */
+  char *date; /**< Date of original recording as a string */
+  char *filename; /**< Original filename of this track */
+  uint16_t tracknumber; /**< Track number (in sequence on recording) */
+  uint32_t duration; /**< Duration in milliseconds */
+  uint32_t samplerate; /**< Sample rate of original file, min 0x1f80 max 0xbb80 */
+  uint16_t nochannels; /**< Number of channels in this recording 0 = unknown, 1 or 2 */
+  uint32_t wavecodec; /**< FourCC wave codec name */
+  uint32_t bitrate; /**< (Average) bitrate for this file min=1 max=0x16e360 */
+  uint16_t bitratetype; /**< 0 = unused, 1 = constant, 2 = VBR, 3 = free */
+  uint16_t rating; /**< User rating 0-100 (0x00-0x64) */
+  uint32_t usecount; /**< Number of times used/played */
+  uint64_t filesize; /**< Size of track file in bytes */
+  time_t modificationdate; /**< Date of last alteration of the track */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the current track */
+  LIBMTP_track_t *next; /**< Next track in list or NULL if last track */
+};
+
+/**
+ * MTP Playlist structure
+ */
+struct LIBMTP_playlist_struct {
+  uint32_t playlist_id; /**< Unique playlist ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this playlist */
+  char *name; /**< Name of playlist */
+  uint32_t *tracks; /**< The tracks in this playlist */
+  uint32_t no_tracks; /**< The number of tracks in this playlist */
+  LIBMTP_playlist_t *next; /**< Next playlist or NULL if last playlist */
+};
+
+/**
+ * MTP Album structure
+ */
+struct LIBMTP_album_struct {
+  uint32_t album_id; /**< Unique playlist ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this album */
+  char *name; /**< Name of album */
+  char *artist; /**< Name of album artist */
+  char *composer; /**< Name of recording composer */
+  char *genre; /**< Genre of album */
+  uint32_t *tracks; /**< The tracks in this album */
+  uint32_t no_tracks; /**< The number of tracks in this album */
+  LIBMTP_album_t *next; /**< Next album or NULL if last album */
+};
+
+/**
+ * MTP Folder structure
+ */
+struct LIBMTP_folder_struct {
+  uint32_t folder_id; /**< Unique folder ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this folder */
+  char *name; /**< Name of folder */
+  LIBMTP_folder_t *sibling; /**< Next folder at same level or NULL if no more */
+  LIBMTP_folder_t *child; /**< Child folder or NULL if no children */
+};
+
+/**
+ * LIBMTP Object RepresentativeSampleData Structure
+ */
+struct LIBMTP_filesampledata_struct {
+  uint32_t width; /**< Width of sample if it is an image */
+  uint32_t height; /**< Height of sample if it is an image */
+  uint32_t duration; /**< Duration in milliseconds if it is audio */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the sample */
+  uint64_t size; /**< Size of sample data in bytes */
+  char *data; /**< Sample data */
+};
+
+/**
+ * LIBMTP Device Storage structure
+ */
+struct LIBMTP_devicestorage_struct {
+  uint32_t id; /**< Unique ID for this storage */
+  uint16_t StorageType; /**< Storage type */
+  uint16_t FilesystemType; /**< Filesystem type */
+  uint16_t AccessCapability; /**< Access capability */
+  uint64_t MaxCapacity; /**< Maximum capability */
+  uint64_t FreeSpaceInBytes; /**< Free space in bytes */
+  uint64_t FreeSpaceInObjects; /**< Free space in objects */
+  char *StorageDescription; /**< A brief description of this storage */
+  char *VolumeIdentifier; /**< A volume identifier */
+  LIBMTP_devicestorage_t *next; /**< Next storage, follow this link until NULL */
+  LIBMTP_devicestorage_t *prev; /**< Previous storage */
+};
+  
+
+/** @} */
+
+/* Make functions available for C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup internals The libmtp internals API.
+ * @{
+ */
+void LIBMTP_Init(void);
+int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const, int * const);
+/**
+ * @}
+ * @defgroup basic The basic device management API.
+ * @{
+ */
+LIBMTP_error_number_t LIBMTP_Detect_Raw_Devices(LIBMTP_raw_device_t **, int *);
+LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *);
+/* Begin old, legacy interface */
+LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void);
+LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **);
+uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *);
+void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t*);
+/* End old, legacy interface */
+void LIBMTP_Release_Device(LIBMTP_mtpdevice_t*);
+void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t*);
+int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Manufacturername(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t*);
+int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t*, char const * const);
+char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t*);
+int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t*, char const * const);
+int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *,
+			    uint8_t * const,
+			    uint8_t * const);
+int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *, char ** const);
+int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *, char ** const);
+int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *, uint16_t ** const, uint16_t * const);
+LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t*);
+void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t*);
+void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t*);
+
+void LIBMTP_Set_Device_Timeout(LIBMTP_mtpdevice_t *device, int milliseconds);
+void LIBMTP_Get_Device_Timeout(LIBMTP_mtpdevice_t *device, int * milliseconds);
+
+#define LIBMTP_STORAGE_SORTBY_NOTSORTED 0
+#define LIBMTP_STORAGE_SORTBY_FREESPACE 1
+#define LIBMTP_STORAGE_SORTBY_MAXSPACE  2
+
+int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *, int const);
+int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *, LIBMTP_devicestorage_t *);
+
+/**
+ * Get/set arbitrary properties.  These do not update the cache; should only be used on
+ * properties not stored in structs
+ */
+char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *, uint32_t const, LIBMTP_property_t const);
+uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint64_t const);
+uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint32_t const);
+uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint16_t const);
+uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint8_t const);
+int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, char const * const);
+int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint32_t const);
+int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint16_t const);
+int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint8_t const);
+char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty);
+int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t*, LIBMTP_property_t const,
+            LIBMTP_filetype_t const);
+int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t*, LIBMTP_property_t const,
+            LIBMTP_filetype_t const, LIBMTP_allowed_values_t*);
+void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t*);
+
+/**
+ * @}
+ * @defgroup files The file management API.
+ * @{
+ */
+LIBMTP_file_t *LIBMTP_new_file_t(void);
+void LIBMTP_destroy_file_t(LIBMTP_file_t*);
+char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t);
+LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *);
+LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *,
+      LIBMTP_progressfunc_t const, void const * const);
+LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t*, uint32_t, char const * const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t*, uint32_t const, int const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *, uint32_t const, MTPDataPutFunc, void *,
+                   LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *, char const * const,
+	                 LIBMTP_file_t * const, LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *, int const,
+	                LIBMTP_file_t * const, LIBMTP_progressfunc_t const,
+			void const * const);
+int LIBMTP_Send_File_From_Handler(LIBMTP_mtpdevice_t *, MTPDataGetFunc, void *,
+      LIBMTP_file_t * const, LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Set_File_Name(LIBMTP_mtpdevice_t *, LIBMTP_file_t *, const char *);
+LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void);
+void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t *);
+int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *,
+                        LIBMTP_filetype_t const,
+                        LIBMTP_filesampledata_t **);
+int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *, uint32_t const,
+                          LIBMTP_filesampledata_t *);
+int LIBMTP_Get_Representative_Sample(LIBMTP_mtpdevice_t *, uint32_t const,
+                          LIBMTP_filesampledata_t *);
+
+
+void LIBMTP_Set_Load_Cache_On_Demand(int flag);
+
+LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device,
+      uint32_t storageId, uint32_t parentId);
+
+/**
+ * @}
+ * @defgroup tracks The track management API.
+ * @{
+ */
+LIBMTP_track_t *LIBMTP_new_track_t(void);
+void LIBMTP_destroy_track_t(LIBMTP_track_t*);
+LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t*);
+LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t*,
+      LIBMTP_progressfunc_t const, void const * const);
+LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t*, uint32_t const);
+int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t*, uint32_t, char const * const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t*, uint32_t const, int const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_Track_To_Handler(LIBMTP_mtpdevice_t *, uint32_t const, MTPDataPutFunc,
+      void *, LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *,
+			 char const * const, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *,
+			 int const, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_Track_From_Handler(LIBMTP_mtpdevice_t *,
+			 MTPDataGetFunc, void *, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *,
+			LIBMTP_track_t const * const);
+int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *, uint32_t);
+int LIBMTP_Set_Track_Name(LIBMTP_mtpdevice_t *, LIBMTP_track_t *, const char *);
+/** @} */
+
+/**
+ * @}
+ * @defgroup folders The folder management API.
+ * @{
+ */
+LIBMTP_folder_t *LIBMTP_new_folder_t(void);
+void LIBMTP_destroy_folder_t(LIBMTP_folder_t*);
+LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t*);
+LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t*, uint32_t const);
+uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t*, char *, uint32_t, uint32_t);
+int LIBMTP_Set_Folder_Name(LIBMTP_mtpdevice_t *, LIBMTP_folder_t *, const char *);
+/** @} */
+
+
+/**
+ * @}
+ * @defgroup playlists The audio/video playlist management API.
+ * @{
+ */
+LIBMTP_playlist_t *LIBMTP_new_playlist_t(void);
+void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *);
+LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *);
+LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t * const);
+int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t * const);
+int LIBMTP_Set_Playlist_Name(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t *, const char *);
+
+/**
+ * @}
+ * @defgroup albums The audio/video album management API.
+ * @{
+ */
+LIBMTP_album_t *LIBMTP_new_album_t(void);
+void LIBMTP_destroy_album_t(LIBMTP_album_t *);
+LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *);
+LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *, LIBMTP_album_t * const);
+int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *, LIBMTP_album_t const * const);
+int LIBMTP_Set_Album_Name(LIBMTP_mtpdevice_t *, LIBMTP_album_t *, const char *);
+
+/**
+ * @}
+ * @defgroup objects The object management API.
+ * @{
+ */
+int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *, uint32_t);
+int LIBMTP_Set_Object_Filename(LIBMTP_mtpdevice_t *, uint32_t , char *);
+
+/** @} */
+
+/* End of C++ exports */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBMTP_H_INCLUSION_GUARD */
+
diff --git a/src/libmtp.h.in b/src/libmtp.h.in
new file mode 100644
index 0000000..12f657d
--- /dev/null
+++ b/src/libmtp.h.in
@@ -0,0 +1,930 @@
+/**
+ * \file libmtp.h
+ * Interface to the Media Transfer Protocol library.
+ *
+ * Copyright (C) 2005-2008 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
+ * Copyright (C) 2008 Florent Mertens <flomertens@gmail.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * <code>
+ * #include <libmtp.h>
+ * </code>
+ */
+#ifndef LIBMTP_H_INCLUSION_GUARD
+#define LIBMTP_H_INCLUSION_GUARD
+
+#define LIBMTP_VERSION @VERSION@
+#define LIBMTP_VERSION_STRING "@VERSION@"
+
+/* This handles MSVC pecularities */
+#ifdef _MSC_VER
+#include <windows.h>
+#define __WIN32__
+#define snprintf _snprintf
+#define ssize_t SSIZE_T
+/*
+ * Types that do not exist in Windows
+ * sys/types.h, but they exist in mingw32
+ * sys/types.h.
+ */
+typedef char int8_t;
+typedef unsigned char uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+
+#include <stdio.h>
+#include <usb.h>
+#include <stdint.h>
+
+/**
+ * @defgroup types libmtp global type definitions
+ * @{
+ * The filetypes defined here are the external types used
+ * by the libmtp library interface. The types used internally
+ * as PTP-defined enumerator types is something different.
+ */
+typedef enum {
+  LIBMTP_FILETYPE_WAV,
+  LIBMTP_FILETYPE_MP3,
+  LIBMTP_FILETYPE_WMA,
+  LIBMTP_FILETYPE_OGG,
+  LIBMTP_FILETYPE_AUDIBLE,
+  LIBMTP_FILETYPE_MP4,
+  LIBMTP_FILETYPE_UNDEF_AUDIO,
+  LIBMTP_FILETYPE_WMV,
+  LIBMTP_FILETYPE_AVI,
+  LIBMTP_FILETYPE_MPEG,
+  LIBMTP_FILETYPE_ASF,
+  LIBMTP_FILETYPE_QT,
+  LIBMTP_FILETYPE_UNDEF_VIDEO,
+  LIBMTP_FILETYPE_JPEG,
+  LIBMTP_FILETYPE_JFIF,
+  LIBMTP_FILETYPE_TIFF,
+  LIBMTP_FILETYPE_BMP,
+  LIBMTP_FILETYPE_GIF,
+  LIBMTP_FILETYPE_PICT,
+  LIBMTP_FILETYPE_PNG,
+  LIBMTP_FILETYPE_VCALENDAR1,
+  LIBMTP_FILETYPE_VCALENDAR2,
+  LIBMTP_FILETYPE_VCARD2,
+  LIBMTP_FILETYPE_VCARD3,
+  LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT,
+  LIBMTP_FILETYPE_WINEXEC,
+  LIBMTP_FILETYPE_TEXT,
+  LIBMTP_FILETYPE_HTML,
+  LIBMTP_FILETYPE_FIRMWARE,
+  LIBMTP_FILETYPE_AAC,
+  LIBMTP_FILETYPE_MEDIACARD,
+  LIBMTP_FILETYPE_FLAC,
+  LIBMTP_FILETYPE_MP2,
+  LIBMTP_FILETYPE_M4A,
+  LIBMTP_FILETYPE_DOC,
+  LIBMTP_FILETYPE_XML,
+  LIBMTP_FILETYPE_XLS,
+  LIBMTP_FILETYPE_PPT,
+  LIBMTP_FILETYPE_MHT,
+  LIBMTP_FILETYPE_JP2,
+  LIBMTP_FILETYPE_JPX,
+  LIBMTP_FILETYPE_ALBUM,
+  LIBMTP_FILETYPE_PLAYLIST,
+  LIBMTP_FILETYPE_UNKNOWN
+} LIBMTP_filetype_t;
+
+/**
+ * \def LIBMTP_FILETYPE_IS_AUDIO
+ * Audio filetype test.
+ *
+ * For filetypes that can be either audio
+ * or video, use LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ */
+#define LIBMTP_FILETYPE_IS_AUDIO(a)\
+(a == LIBMTP_FILETYPE_WAV ||\
+ a == LIBMTP_FILETYPE_MP3 ||\
+ a == LIBMTP_FILETYPE_MP2 ||\
+ a == LIBMTP_FILETYPE_WMA ||\
+ a == LIBMTP_FILETYPE_OGG ||\
+ a == LIBMTP_FILETYPE_FLAC ||\
+ a == LIBMTP_FILETYPE_AAC ||\
+ a == LIBMTP_FILETYPE_M4A ||\
+ a == LIBMTP_FILETYPE_AUDIBLE ||\
+ a == LIBMTP_FILETYPE_UNDEF_AUDIO)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_VIDEO
+ *  Video filetype test.
+ *
+ * For filetypes that can be either audio
+ * or video, use LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ */
+#define LIBMTP_FILETYPE_IS_VIDEO(a)\
+(a == LIBMTP_FILETYPE_WMV ||\
+ a == LIBMTP_FILETYPE_AVI ||\
+ a == LIBMTP_FILETYPE_MPEG ||\
+ a == LIBMTP_FILETYPE_UNDEF_VIDEO)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_AUDIOVIDEO
+ *  Audio and&slash;or video filetype test.
+ */
+#define LIBMTP_FILETYPE_IS_AUDIOVIDEO(a)\
+(a == LIBMTP_FILETYPE_MP4 ||\
+ a == LIBMTP_FILETYPE_ASF ||\
+ a == LIBMTP_FILETYPE_QT)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_TRACK
+ *  Test if filetype is a track.
+ *  Use this to determine if the File API or Track API
+ *  should be used to upload or download an object.
+ */
+#define LIBMTP_FILETYPE_IS_TRACK(a)\
+(LIBMTP_FILETYPE_IS_AUDIO(a) ||\
+ LIBMTP_FILETYPE_IS_VIDEO(a) ||\
+ LIBMTP_FILETYPE_IS_AUDIOVIDEO(a))
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_IMAGE
+ *  Image filetype test
+ */
+#define LIBMTP_FILETYPE_IS_IMAGE(a)\
+(a == LIBMTP_FILETYPE_JPEG ||\
+a == LIBMTP_FILETYPE_JFIF ||\
+a == LIBMTP_FILETYPE_TIFF ||\
+a == LIBMTP_FILETYPE_BMP ||\
+a == LIBMTP_FILETYPE_GIF ||\
+a == LIBMTP_FILETYPE_PICT ||\
+a == LIBMTP_FILETYPE_PNG ||\
+a == LIBMTP_FILETYPE_JP2 ||\
+a == LIBMTP_FILETYPE_JPX ||\
+a == LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_ADDRESSBOOK
+ *  Addressbook and Business card filetype test
+ */
+#define LIBMTP_FILETYPE_IS_ADDRESSBOOK(a)\
+(a == LIBMTP_FILETYPE_VCARD2 ||\
+a == LIBMTP_FILETYPE_VCARD2)
+
+/**
+ *  \def LIBMTP_FILETYPE_IS_CALENDAR
+ *  Calendar and Appointment filetype test
+ */
+#define LIBMTP_FILETYPE_IS_CALENDAR(a)\
+(a == LIBMTP_FILETYPE_VCALENDAR1 ||\
+a == LIBMTP_FILETYPE_VCALENDAR2)
+
+/**
+ * The properties defined here are the external types used
+ * by the libmtp library interface.
+ */
+typedef enum {
+  LIBMTP_PROPERTY_StorageID,
+  LIBMTP_PROPERTY_ObjectFormat,
+  LIBMTP_PROPERTY_ProtectionStatus,
+  LIBMTP_PROPERTY_ObjectSize,
+  LIBMTP_PROPERTY_AssociationType,
+  LIBMTP_PROPERTY_AssociationDesc,
+  LIBMTP_PROPERTY_ObjectFileName,
+  LIBMTP_PROPERTY_DateCreated,
+  LIBMTP_PROPERTY_DateModified,
+  LIBMTP_PROPERTY_Keywords,
+  LIBMTP_PROPERTY_ParentObject,
+  LIBMTP_PROPERTY_AllowedFolderContents,
+  LIBMTP_PROPERTY_Hidden,
+  LIBMTP_PROPERTY_SystemObject,
+  LIBMTP_PROPERTY_PersistantUniqueObjectIdentifier,
+  LIBMTP_PROPERTY_SyncID,
+  LIBMTP_PROPERTY_PropertyBag,
+  LIBMTP_PROPERTY_Name,
+  LIBMTP_PROPERTY_CreatedBy,
+  LIBMTP_PROPERTY_Artist,
+  LIBMTP_PROPERTY_DateAuthored,
+  LIBMTP_PROPERTY_Description,
+  LIBMTP_PROPERTY_URLReference,
+  LIBMTP_PROPERTY_LanguageLocale,
+  LIBMTP_PROPERTY_CopyrightInformation,
+  LIBMTP_PROPERTY_Source,
+  LIBMTP_PROPERTY_OriginLocation,
+  LIBMTP_PROPERTY_DateAdded,
+  LIBMTP_PROPERTY_NonConsumable,
+  LIBMTP_PROPERTY_CorruptOrUnplayable,
+  LIBMTP_PROPERTY_ProducerSerialNumber,
+  LIBMTP_PROPERTY_RepresentativeSampleFormat,
+  LIBMTP_PROPERTY_RepresentativeSampleSize,
+  LIBMTP_PROPERTY_RepresentativeSampleHeight,
+  LIBMTP_PROPERTY_RepresentativeSampleWidth,
+  LIBMTP_PROPERTY_RepresentativeSampleDuration,
+  LIBMTP_PROPERTY_RepresentativeSampleData,
+  LIBMTP_PROPERTY_Width,
+  LIBMTP_PROPERTY_Height,
+  LIBMTP_PROPERTY_Duration,
+  LIBMTP_PROPERTY_Rating,
+  LIBMTP_PROPERTY_Track,
+  LIBMTP_PROPERTY_Genre,
+  LIBMTP_PROPERTY_Credits,
+  LIBMTP_PROPERTY_Lyrics,
+  LIBMTP_PROPERTY_SubscriptionContentID,
+  LIBMTP_PROPERTY_ProducedBy,
+  LIBMTP_PROPERTY_UseCount,
+  LIBMTP_PROPERTY_SkipCount,
+  LIBMTP_PROPERTY_LastAccessed,
+  LIBMTP_PROPERTY_ParentalRating,
+  LIBMTP_PROPERTY_MetaGenre,
+  LIBMTP_PROPERTY_Composer,
+  LIBMTP_PROPERTY_EffectiveRating,
+  LIBMTP_PROPERTY_Subtitle,
+  LIBMTP_PROPERTY_OriginalReleaseDate,
+  LIBMTP_PROPERTY_AlbumName,
+  LIBMTP_PROPERTY_AlbumArtist,
+  LIBMTP_PROPERTY_Mood,
+  LIBMTP_PROPERTY_DRMStatus,
+  LIBMTP_PROPERTY_SubDescription,
+  LIBMTP_PROPERTY_IsCropped,
+  LIBMTP_PROPERTY_IsColorCorrected,
+  LIBMTP_PROPERTY_ImageBitDepth,
+  LIBMTP_PROPERTY_Fnumber,
+  LIBMTP_PROPERTY_ExposureTime,
+  LIBMTP_PROPERTY_ExposureIndex,
+  LIBMTP_PROPERTY_DisplayName,
+  LIBMTP_PROPERTY_BodyText,
+  LIBMTP_PROPERTY_Subject,
+  LIBMTP_PROPERTY_Priority,
+  LIBMTP_PROPERTY_GivenName,
+  LIBMTP_PROPERTY_MiddleNames,
+  LIBMTP_PROPERTY_FamilyName,
+  LIBMTP_PROPERTY_Prefix,
+  LIBMTP_PROPERTY_Suffix,
+  LIBMTP_PROPERTY_PhoneticGivenName,
+  LIBMTP_PROPERTY_PhoneticFamilyName,
+  LIBMTP_PROPERTY_EmailPrimary,
+  LIBMTP_PROPERTY_EmailPersonal1,
+  LIBMTP_PROPERTY_EmailPersonal2,
+  LIBMTP_PROPERTY_EmailBusiness1,
+  LIBMTP_PROPERTY_EmailBusiness2,
+  LIBMTP_PROPERTY_EmailOthers,
+  LIBMTP_PROPERTY_PhoneNumberPrimary,
+  LIBMTP_PROPERTY_PhoneNumberPersonal,
+  LIBMTP_PROPERTY_PhoneNumberPersonal2,
+  LIBMTP_PROPERTY_PhoneNumberBusiness,
+  LIBMTP_PROPERTY_PhoneNumberBusiness2,
+  LIBMTP_PROPERTY_PhoneNumberMobile,
+  LIBMTP_PROPERTY_PhoneNumberMobile2,
+  LIBMTP_PROPERTY_FaxNumberPrimary,
+  LIBMTP_PROPERTY_FaxNumberPersonal,
+  LIBMTP_PROPERTY_FaxNumberBusiness,
+  LIBMTP_PROPERTY_PagerNumber,
+  LIBMTP_PROPERTY_PhoneNumberOthers,
+  LIBMTP_PROPERTY_PrimaryWebAddress,
+  LIBMTP_PROPERTY_PersonalWebAddress,
+  LIBMTP_PROPERTY_BusinessWebAddress,
+  LIBMTP_PROPERTY_InstantMessengerAddress,
+  LIBMTP_PROPERTY_InstantMessengerAddress2,
+  LIBMTP_PROPERTY_InstantMessengerAddress3,
+  LIBMTP_PROPERTY_PostalAddressPersonalFull,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullLine1,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullLine2,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullCity,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullRegion,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullPostalCode,
+  LIBMTP_PROPERTY_PostalAddressPersonalFullCountry,
+  LIBMTP_PROPERTY_PostalAddressBusinessFull,
+  LIBMTP_PROPERTY_PostalAddressBusinessLine1,
+  LIBMTP_PROPERTY_PostalAddressBusinessLine2,
+  LIBMTP_PROPERTY_PostalAddressBusinessCity,
+  LIBMTP_PROPERTY_PostalAddressBusinessRegion,
+  LIBMTP_PROPERTY_PostalAddressBusinessPostalCode,
+  LIBMTP_PROPERTY_PostalAddressBusinessCountry,
+  LIBMTP_PROPERTY_PostalAddressOtherFull,
+  LIBMTP_PROPERTY_PostalAddressOtherLine1,
+  LIBMTP_PROPERTY_PostalAddressOtherLine2,
+  LIBMTP_PROPERTY_PostalAddressOtherCity,
+  LIBMTP_PROPERTY_PostalAddressOtherRegion,
+  LIBMTP_PROPERTY_PostalAddressOtherPostalCode,
+  LIBMTP_PROPERTY_PostalAddressOtherCountry,
+  LIBMTP_PROPERTY_OrganizationName,
+  LIBMTP_PROPERTY_PhoneticOrganizationName,
+  LIBMTP_PROPERTY_Role,
+  LIBMTP_PROPERTY_Birthdate,
+  LIBMTP_PROPERTY_MessageTo,
+  LIBMTP_PROPERTY_MessageCC,
+  LIBMTP_PROPERTY_MessageBCC,
+  LIBMTP_PROPERTY_MessageRead,
+  LIBMTP_PROPERTY_MessageReceivedTime,
+  LIBMTP_PROPERTY_MessageSender,
+  LIBMTP_PROPERTY_ActivityBeginTime,
+  LIBMTP_PROPERTY_ActivityEndTime,
+  LIBMTP_PROPERTY_ActivityLocation,
+  LIBMTP_PROPERTY_ActivityRequiredAttendees,
+  LIBMTP_PROPERTY_ActivityOptionalAttendees,
+  LIBMTP_PROPERTY_ActivityResources,
+  LIBMTP_PROPERTY_ActivityAccepted,
+  LIBMTP_PROPERTY_Owner,
+  LIBMTP_PROPERTY_Editor,
+  LIBMTP_PROPERTY_Webmaster,
+  LIBMTP_PROPERTY_URLSource,
+  LIBMTP_PROPERTY_URLDestination,
+  LIBMTP_PROPERTY_TimeBookmark,
+  LIBMTP_PROPERTY_ObjectBookmark,
+  LIBMTP_PROPERTY_ByteBookmark,
+  LIBMTP_PROPERTY_LastBuildDate,
+  LIBMTP_PROPERTY_TimetoLive,
+  LIBMTP_PROPERTY_MediaGUID,
+  LIBMTP_PROPERTY_TotalBitRate,
+  LIBMTP_PROPERTY_BitRateType,
+  LIBMTP_PROPERTY_SampleRate,
+  LIBMTP_PROPERTY_NumberOfChannels,
+  LIBMTP_PROPERTY_AudioBitDepth,
+  LIBMTP_PROPERTY_ScanDepth,
+  LIBMTP_PROPERTY_AudioWAVECodec,
+  LIBMTP_PROPERTY_AudioBitRate,
+  LIBMTP_PROPERTY_VideoFourCCCodec,
+  LIBMTP_PROPERTY_VideoBitRate,
+  LIBMTP_PROPERTY_FramesPerThousandSeconds,
+  LIBMTP_PROPERTY_KeyFrameDistance,
+  LIBMTP_PROPERTY_BufferSize,
+  LIBMTP_PROPERTY_EncodingQuality,
+  LIBMTP_PROPERTY_EncodingProfile,
+  LIBMTP_PROPERTY_BuyFlag,
+  LIBMTP_PROPERTY_UNKNOWN
+} LIBMTP_property_t;
+
+/**
+ * These are the data types
+ */
+typedef enum {
+  LIBMTP_DATATYPE_INT8,
+  LIBMTP_DATATYPE_UINT8,
+  LIBMTP_DATATYPE_INT16,
+  LIBMTP_DATATYPE_UINT16,
+  LIBMTP_DATATYPE_INT32,
+  LIBMTP_DATATYPE_UINT32,
+  LIBMTP_DATATYPE_INT64,
+  LIBMTP_DATATYPE_UINT64,
+} LIBMTP_datatype_t;
+
+/**
+ * These are the numbered error codes. You can also
+ * get string representations for errors.
+ */
+typedef enum {
+  LIBMTP_ERROR_NONE,
+  LIBMTP_ERROR_GENERAL,
+  LIBMTP_ERROR_PTP_LAYER,
+  LIBMTP_ERROR_USB_LAYER,
+  LIBMTP_ERROR_MEMORY_ALLOCATION,
+  LIBMTP_ERROR_NO_DEVICE_ATTACHED,
+  LIBMTP_ERROR_STORAGE_FULL,
+  LIBMTP_ERROR_CONNECTING,
+  LIBMTP_ERROR_CANCELLED
+} LIBMTP_error_number_t;
+typedef struct LIBMTP_device_entry_struct LIBMTP_device_entry_t; /**< @see LIBMTP_device_entry_struct */
+typedef struct LIBMTP_raw_device_struct LIBMTP_raw_device_t; /**< @see LIBMTP_raw_device_struct */
+typedef struct LIBMTP_error_struct LIBMTP_error_t; /**< @see LIBMTP_error_struct */
+typedef struct LIBMTP_allowed_values_struct LIBMTP_allowed_values_t; /**< @see LIBMTP_allowed_values_struct */
+typedef struct LIBMTP_mtpdevice_struct LIBMTP_mtpdevice_t; /**< @see LIBMTP_mtpdevice_struct */
+typedef struct LIBMTP_file_struct LIBMTP_file_t; /**< @see LIBMTP_file_struct */
+typedef struct LIBMTP_track_struct LIBMTP_track_t; /**< @see LIBMTP_track_struct */
+typedef struct LIBMTP_playlist_struct LIBMTP_playlist_t; /**< @see LIBMTP_playlist_struct */
+typedef struct LIBMTP_album_struct LIBMTP_album_t; /**< @see LIBMTP_album_struct */
+typedef struct LIBMTP_folder_struct LIBMTP_folder_t; /**< @see LIBMTP_folder_t */
+typedef struct LIBMTP_object_struct LIBMTP_object_t; /**< @see LIBMTP_object_t */
+typedef struct LIBMTP_filesampledata_struct LIBMTP_filesampledata_t; /**< @see LIBMTP_filesample_t */
+typedef struct LIBMTP_devicestorage_struct LIBMTP_devicestorage_t; /**< @see LIBMTP_devicestorage_t */
+
+/**
+ * The callback type definition. Notice that a progress percentage ratio
+ * is easy to calculate by dividing <code>sent</code> by
+ * <code>total</code>.
+ * @param sent the number of bytes sent so far
+ * @param total the total number of bytes to send
+ * @param data a user-defined dereferencable pointer
+ * @return if anything else than 0 is returned, the current transfer will be
+ *         interrupted / cancelled.
+ */
+typedef int (* LIBMTP_progressfunc_t) (uint64_t const sent, uint64_t const total,
+                		void const * const data);
+
+/**
+ * Callback function for get by handler function
+ * @param params the device parameters
+ * @param priv a user-defined dereferencable pointer
+ * @param wantlen the number of bytes wanted
+ * @param data a buffer to write the data to
+ * @param gotlen pointer to the number of bytes actually written
+ *        to data
+ * @return LIBMTP_HANDLER_RETURN_OK if successful,
+ *         LIBMTP_HANDLER_RETURN_ERROR on error or
+ *         LIBMTP_HANDLER_RETURN_CANCEL to cancel the transfer
+ */
+typedef uint16_t (* MTPDataGetFunc)	(void* params, void* priv,
+					uint32_t wantlen, unsigned char *data, uint32_t *gotlen);
+
+/**
+ * Callback function for put by handler function
+ * @param params the device parameters
+ * @param priv a user-defined dereferencable pointer
+ * @param sendlen the number of bytes available
+ * @param data a buffer to read the data from
+ * @param putlen pointer to the number of bytes actually read
+ *        from data
+ * @return LIBMTP_HANDLER_RETURN_OK if successful,
+ *         LIBMTP_HANDLER_RETURN_ERROR on error or
+ *         LIBMTP_HANDLER_RETURN_CANCEL to cancel the transfer
+ */
+typedef uint16_t (* MTPDataPutFunc)	(void* params, void* priv,
+					uint32_t sendlen, unsigned char *data, uint32_t *putlen);
+
+/**
+ * The return codes for the get/put functions
+ */
+#define LIBMTP_HANDLER_RETURN_OK 0
+#define LIBMTP_HANDLER_RETURN_ERROR 1
+#define LIBMTP_HANDLER_RETURN_CANCEL 2
+
+/**
+ * @}
+ * @defgroup structar libmtp data structures
+ * @{
+ */
+
+/**
+ * A data structure to hold MTP device entries.
+ */
+struct LIBMTP_device_entry_struct {
+  char *vendor; /**< The vendor of this device */
+  uint16_t vendor_id; /**< Vendor ID for this device */
+  char *product; /**< The product name of this device */
+  uint16_t product_id; /**< Product ID for this device */
+  uint32_t device_flags; /**< Bugs, device specifics etc */
+};
+
+/**
+ * A data structure to hold a raw MTP device connected
+ * to the bus.
+ */
+struct LIBMTP_raw_device_struct {
+  LIBMTP_device_entry_t device_entry; /**< The device entry for this raw device */
+  uint32_t bus_location; /**< Location of the bus, if device available */
+  uint8_t devnum; /**< Device number on the bus, if device available */
+};
+
+/**
+ * A data structure to hold errors from the library.
+ */
+struct LIBMTP_error_struct {
+  LIBMTP_error_number_t errornumber;
+  char *error_text;
+  LIBMTP_error_t *next;
+};
+
+/**
+ * A data structure to hold allowed ranges of values
+ */
+struct LIBMTP_allowed_values_struct {
+  uint8_t   u8max;
+  uint8_t   u8min;
+  uint8_t   u8step;
+  uint8_t*  u8vals;
+  int8_t    i8max;
+  int8_t    i8min;
+  int8_t    i8step;
+  int8_t*   i8vals;
+  uint16_t  u16max;
+  uint16_t  u16min;
+  uint16_t  u16step;
+  uint16_t* u16vals;
+  int16_t   i16max;
+  int16_t   i16min;
+  int16_t   i16step;
+  int16_t*  i16vals;
+  uint32_t  u32max;
+  uint32_t  u32min;
+  uint32_t  u32step;
+  uint32_t* u32vals;
+  int32_t   i32max;
+  int32_t   i32min;
+  int32_t   i32step;
+  int32_t*  i32vals;
+  uint64_t  u64max;
+  uint64_t  u64min;
+  uint64_t  u64step;
+  uint64_t* u64vals;
+  int64_t   i64max;
+  int64_t   i64min;
+  int64_t   i64step;
+  int64_t*  i64vals;
+  /**
+   * Number of entries in the vals array
+   */
+  uint16_t  num_entries;
+  /**
+   * The datatype specifying which of the above is used
+  */
+  LIBMTP_datatype_t datatype;
+  /**
+   * Non zero for range, 0 for enum
+  */
+  int is_range;
+};
+
+/**
+ * Main MTP device object struct
+ */
+struct LIBMTP_mtpdevice_struct {
+  /**
+   * Object bitsize, typically 32 or 64.
+   */
+  uint8_t object_bitsize;
+  /**
+   * Parameters for this device, must be cast into
+   * \c (PTPParams*) before internal use.
+   */
+  void *params;
+  /**
+   * USB device for this device, must be cast into
+   * \c (PTP_USB*) before internal use.
+   */
+  void *usbinfo;
+  /** 
+   * The storage for this device, do not use strings in here without 
+   * copying them first, and beware that this list may be rebuilt at
+   * any time.
+   * @see LIBMTP_Get_Storage()
+   */
+  LIBMTP_devicestorage_t *storage;
+  /**
+   * The error stack. This shall be handled using the error getting
+   * and clearing functions, not by dereferencing this list.
+   */
+  LIBMTP_error_t *errorstack;
+  /** The maximum battery level for this device */
+  uint8_t maximum_battery_level;
+  /** Default music folder */
+  uint32_t default_music_folder;
+  /** Default playlist folder */
+  uint32_t default_playlist_folder;
+  /** Default picture folder */
+  uint32_t default_picture_folder;
+  /** Default video folder */
+  uint32_t default_video_folder;
+  /** Default organizer folder */
+  uint32_t default_organizer_folder;
+  /** Default ZENcast folder (only Creative devices...) */
+  uint32_t default_zencast_folder;
+  /** Default Album folder */
+  uint32_t default_album_folder;
+  /** Default Text folder */
+  uint32_t default_text_folder;
+  /** Per device iconv() converters, only used internally */
+  void *cd;
+  
+  /** Pointer to next device in linked list; NULL if this is the last device */
+  LIBMTP_mtpdevice_t *next;
+};
+
+/**
+ * MTP file struct
+ */
+struct LIBMTP_file_struct {
+  uint32_t item_id; /**< Unique item ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this file */
+  char *filename; /**< Filename of this file */
+  uint64_t filesize; /**< Size of file in bytes */
+  time_t modificationdate; /**< Date of last alteration of the file */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the current file */
+  LIBMTP_file_t *next; /**< Next file in list or NULL if last file */
+};
+
+/**
+ * MTP track struct
+ */
+struct LIBMTP_track_struct {
+  uint32_t item_id; /**< Unique item ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this track */
+  char *title; /**< Track title */
+  char *artist; /**< Name of recording artist */
+  char *composer; /**< Name of recording composer */
+  char *genre; /**< Genre name for track */
+  char *album; /**< Album name for track */
+  char *date; /**< Date of original recording as a string */
+  char *filename; /**< Original filename of this track */
+  uint16_t tracknumber; /**< Track number (in sequence on recording) */
+  uint32_t duration; /**< Duration in milliseconds */
+  uint32_t samplerate; /**< Sample rate of original file, min 0x1f80 max 0xbb80 */
+  uint16_t nochannels; /**< Number of channels in this recording 0 = unknown, 1 or 2 */
+  uint32_t wavecodec; /**< FourCC wave codec name */
+  uint32_t bitrate; /**< (Average) bitrate for this file min=1 max=0x16e360 */
+  uint16_t bitratetype; /**< 0 = unused, 1 = constant, 2 = VBR, 3 = free */
+  uint16_t rating; /**< User rating 0-100 (0x00-0x64) */
+  uint32_t usecount; /**< Number of times used/played */
+  uint64_t filesize; /**< Size of track file in bytes */
+  time_t modificationdate; /**< Date of last alteration of the track */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the current track */
+  LIBMTP_track_t *next; /**< Next track in list or NULL if last track */
+};
+
+/**
+ * MTP Playlist structure
+ */
+struct LIBMTP_playlist_struct {
+  uint32_t playlist_id; /**< Unique playlist ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this playlist */
+  char *name; /**< Name of playlist */
+  uint32_t *tracks; /**< The tracks in this playlist */
+  uint32_t no_tracks; /**< The number of tracks in this playlist */
+  LIBMTP_playlist_t *next; /**< Next playlist or NULL if last playlist */
+};
+
+/**
+ * MTP Album structure
+ */
+struct LIBMTP_album_struct {
+  uint32_t album_id; /**< Unique playlist ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this album */
+  char *name; /**< Name of album */
+  char *artist; /**< Name of album artist */
+  char *composer; /**< Name of recording composer */
+  char *genre; /**< Genre of album */
+  uint32_t *tracks; /**< The tracks in this album */
+  uint32_t no_tracks; /**< The number of tracks in this album */
+  LIBMTP_album_t *next; /**< Next album or NULL if last album */
+};
+
+/**
+ * MTP Folder structure
+ */
+struct LIBMTP_folder_struct {
+  uint32_t folder_id; /**< Unique folder ID */
+  uint32_t parent_id; /**< ID of parent folder */
+  uint32_t storage_id; /**< ID of storage holding this folder */
+  char *name; /**< Name of folder */
+  LIBMTP_folder_t *sibling; /**< Next folder at same level or NULL if no more */
+  LIBMTP_folder_t *child; /**< Child folder or NULL if no children */
+};
+
+/**
+ * LIBMTP Object RepresentativeSampleData Structure
+ */
+struct LIBMTP_filesampledata_struct {
+  uint32_t width; /**< Width of sample if it is an image */
+  uint32_t height; /**< Height of sample if it is an image */
+  uint32_t duration; /**< Duration in milliseconds if it is audio */
+  LIBMTP_filetype_t filetype; /**< Filetype used for the sample */
+  uint64_t size; /**< Size of sample data in bytes */
+  char *data; /**< Sample data */
+};
+
+/**
+ * LIBMTP Device Storage structure
+ */
+struct LIBMTP_devicestorage_struct {
+  uint32_t id; /**< Unique ID for this storage */
+  uint16_t StorageType; /**< Storage type */
+  uint16_t FilesystemType; /**< Filesystem type */
+  uint16_t AccessCapability; /**< Access capability */
+  uint64_t MaxCapacity; /**< Maximum capability */
+  uint64_t FreeSpaceInBytes; /**< Free space in bytes */
+  uint64_t FreeSpaceInObjects; /**< Free space in objects */
+  char *StorageDescription; /**< A brief description of this storage */
+  char *VolumeIdentifier; /**< A volume identifier */
+  LIBMTP_devicestorage_t *next; /**< Next storage, follow this link until NULL */
+  LIBMTP_devicestorage_t *prev; /**< Previous storage */
+};
+  
+
+/** @} */
+
+/* Make functions available for C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup internals The libmtp internals API.
+ * @{
+ */
+void LIBMTP_Init(void);
+int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const, int * const);
+/**
+ * @}
+ * @defgroup basic The basic device management API.
+ * @{
+ */
+LIBMTP_error_number_t LIBMTP_Detect_Raw_Devices(LIBMTP_raw_device_t **, int *);
+LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *);
+/* Begin old, legacy interface */
+LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void);
+LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **);
+uint32_t LIBMTP_Number_Devices_In_List(LIBMTP_mtpdevice_t *);
+void LIBMTP_Release_Device_List(LIBMTP_mtpdevice_t*);
+/* End old, legacy interface */
+void LIBMTP_Release_Device(LIBMTP_mtpdevice_t*);
+void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t*);
+int LIBMTP_Reset_Device(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Manufacturername(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Modelname(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Serialnumber(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t*);
+char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t*);
+int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t*, char const * const);
+char *LIBMTP_Get_Syncpartner(LIBMTP_mtpdevice_t*);
+int LIBMTP_Set_Syncpartner(LIBMTP_mtpdevice_t*, char const * const);
+int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t *,
+			    uint8_t * const,
+			    uint8_t * const);
+int LIBMTP_Get_Secure_Time(LIBMTP_mtpdevice_t *, char ** const);
+int LIBMTP_Get_Device_Certificate(LIBMTP_mtpdevice_t *, char ** const);
+int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *, uint16_t ** const, uint16_t * const);
+LIBMTP_error_t *LIBMTP_Get_Errorstack(LIBMTP_mtpdevice_t*);
+void LIBMTP_Clear_Errorstack(LIBMTP_mtpdevice_t*);
+void LIBMTP_Dump_Errorstack(LIBMTP_mtpdevice_t*);
+
+#define LIBMTP_STORAGE_SORTBY_NOTSORTED 0
+#define LIBMTP_STORAGE_SORTBY_FREESPACE 1
+#define LIBMTP_STORAGE_SORTBY_MAXSPACE  2
+
+int LIBMTP_Get_Storage(LIBMTP_mtpdevice_t *, int const);
+int LIBMTP_Format_Storage(LIBMTP_mtpdevice_t *, LIBMTP_devicestorage_t *);
+
+/**
+ * Get/set arbitrary properties.  These do not update the cache; should only be used on
+ * properties not stored in structs
+ */
+char *LIBMTP_Get_String_From_Object(LIBMTP_mtpdevice_t *, uint32_t const, LIBMTP_property_t const);
+uint64_t LIBMTP_Get_u64_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint64_t const);
+uint32_t LIBMTP_Get_u32_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint32_t const);
+uint16_t LIBMTP_Get_u16_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint16_t const);
+uint8_t LIBMTP_Get_u8_From_Object(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint8_t const);
+int LIBMTP_Set_Object_String(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, char const * const);
+int LIBMTP_Set_Object_u32(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint32_t const);
+int LIBMTP_Set_Object_u16(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint16_t const);
+int LIBMTP_Set_Object_u8(LIBMTP_mtpdevice_t *, uint32_t const,
+      LIBMTP_property_t const, uint8_t const);
+char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty);
+int LIBMTP_Is_Property_Supported(LIBMTP_mtpdevice_t*, LIBMTP_property_t const,
+            LIBMTP_filetype_t const);
+int LIBMTP_Get_Allowed_Property_Values(LIBMTP_mtpdevice_t*, LIBMTP_property_t const,
+            LIBMTP_filetype_t const, LIBMTP_allowed_values_t*);
+void LIBMTP_destroy_allowed_values_t(LIBMTP_allowed_values_t*);
+
+/**
+ * @}
+ * @defgroup files The file management API.
+ * @{
+ */
+LIBMTP_file_t *LIBMTP_new_file_t(void);
+void LIBMTP_destroy_file_t(LIBMTP_file_t*);
+char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t);
+LIBMTP_file_t *LIBMTP_Get_Filelisting(LIBMTP_mtpdevice_t *);
+LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *,
+      LIBMTP_progressfunc_t const, void const * const);
+LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Get_File_To_File(LIBMTP_mtpdevice_t*, uint32_t, char const * const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t*, uint32_t const, int const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *, uint32_t const, MTPDataPutFunc, void *,
+                   LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *, char const * const,
+	                 LIBMTP_file_t * const, LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *, int const,
+	                LIBMTP_file_t * const, LIBMTP_progressfunc_t const,
+			void const * const);
+int LIBMTP_Send_File_From_Handler(LIBMTP_mtpdevice_t *, MTPDataGetFunc, void *,
+      LIBMTP_file_t * const, LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Set_File_Name(LIBMTP_mtpdevice_t *, LIBMTP_file_t *, const char *);
+LIBMTP_filesampledata_t *LIBMTP_new_filesampledata_t(void);
+void LIBMTP_destroy_filesampledata_t(LIBMTP_filesampledata_t *);
+int LIBMTP_Get_Representative_Sample_Format(LIBMTP_mtpdevice_t *,
+                        LIBMTP_filetype_t const,
+                        LIBMTP_filesampledata_t **);
+int LIBMTP_Send_Representative_Sample(LIBMTP_mtpdevice_t *, uint32_t const,
+                          LIBMTP_filesampledata_t *);
+int LIBMTP_Get_Representative_Sample(LIBMTP_mtpdevice_t *, uint32_t const,
+                          LIBMTP_filesampledata_t *);
+
+/**
+ * @}
+ * @defgroup tracks The track management API.
+ * @{
+ */
+LIBMTP_track_t *LIBMTP_new_track_t(void);
+void LIBMTP_destroy_track_t(LIBMTP_track_t*);
+LIBMTP_track_t *LIBMTP_Get_Tracklisting(LIBMTP_mtpdevice_t*);
+LIBMTP_track_t *LIBMTP_Get_Tracklisting_With_Callback(LIBMTP_mtpdevice_t*,
+      LIBMTP_progressfunc_t const, void const * const);
+LIBMTP_track_t *LIBMTP_Get_Trackmetadata(LIBMTP_mtpdevice_t*, uint32_t const);
+int LIBMTP_Get_Track_To_File(LIBMTP_mtpdevice_t*, uint32_t, char const * const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_Track_To_File_Descriptor(LIBMTP_mtpdevice_t*, uint32_t const, int const,
+			LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Get_Track_To_Handler(LIBMTP_mtpdevice_t *, uint32_t const, MTPDataPutFunc,
+      void *, LIBMTP_progressfunc_t const, void const * const);
+int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *,
+			 char const * const, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_Track_From_File_Descriptor(LIBMTP_mtpdevice_t *,
+			 int const, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Send_Track_From_Handler(LIBMTP_mtpdevice_t *,
+			 MTPDataGetFunc, void *, LIBMTP_track_t * const,
+                         LIBMTP_progressfunc_t const,
+			 void const * const);
+int LIBMTP_Update_Track_Metadata(LIBMTP_mtpdevice_t *,
+			LIBMTP_track_t const * const);
+int LIBMTP_Track_Exists(LIBMTP_mtpdevice_t *, uint32_t);
+int LIBMTP_Set_Track_Name(LIBMTP_mtpdevice_t *, LIBMTP_track_t *, const char *);
+/** @} */
+
+/**
+ * @}
+ * @defgroup folders The folder management API.
+ * @{
+ */
+LIBMTP_folder_t *LIBMTP_new_folder_t(void);
+void LIBMTP_destroy_folder_t(LIBMTP_folder_t*);
+LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t*);
+LIBMTP_folder_t *LIBMTP_Find_Folder(LIBMTP_folder_t*, uint32_t const);
+uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t*, char *, uint32_t, uint32_t);
+int LIBMTP_Set_Folder_Name(LIBMTP_mtpdevice_t *, LIBMTP_folder_t *, const char *);
+/** @} */
+
+
+/**
+ * @}
+ * @defgroup playlists The audio/video playlist management API.
+ * @{
+ */
+LIBMTP_playlist_t *LIBMTP_new_playlist_t(void);
+void LIBMTP_destroy_playlist_t(LIBMTP_playlist_t *);
+LIBMTP_playlist_t *LIBMTP_Get_Playlist_List(LIBMTP_mtpdevice_t *);
+LIBMTP_playlist_t *LIBMTP_Get_Playlist(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Create_New_Playlist(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t * const);
+int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t * const);
+int LIBMTP_Set_Playlist_Name(LIBMTP_mtpdevice_t *, LIBMTP_playlist_t *, const char *);
+
+/**
+ * @}
+ * @defgroup albums The audio/video album management API.
+ * @{
+ */
+LIBMTP_album_t *LIBMTP_new_album_t(void);
+void LIBMTP_destroy_album_t(LIBMTP_album_t *);
+LIBMTP_album_t *LIBMTP_Get_Album_List(LIBMTP_mtpdevice_t *);
+LIBMTP_album_t *LIBMTP_Get_Album(LIBMTP_mtpdevice_t *, uint32_t const);
+int LIBMTP_Create_New_Album(LIBMTP_mtpdevice_t *, LIBMTP_album_t * const);
+int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *, LIBMTP_album_t const * const);
+int LIBMTP_Set_Album_Name(LIBMTP_mtpdevice_t *, LIBMTP_album_t *, const char *);
+
+/**
+ * @}
+ * @defgroup objects The object management API.
+ * @{
+ */
+int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *, uint32_t);
+int LIBMTP_Set_Object_Filename(LIBMTP_mtpdevice_t *, uint32_t , char *);
+
+/** @} */
+
+/* End of C++ exports */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBMTP_H_INCLUSION_GUARD */
+
diff --git a/src/libmtp.sym b/src/libmtp.sym
new file mode 100644
index 0000000..8a649a8
--- /dev/null
+++ b/src/libmtp.sym
@@ -0,0 +1,78 @@
+LIBMTP_Init
+LIBMTP_Get_Supported_Devices_List
+LIBMTP_Detect_Raw_Devices
+LIBMTP_Open_Raw_Device
+LIBMTP_Get_First_Device
+LIBMTP_Get_Connected_Devices
+LIBMTP_Number_Devices_In_List
+LIBMTP_Release_Device_List
+LIBMTP_Release_Device
+LIBMTP_Dump_Device_Info
+LIBMTP_Reset_Device
+LIBMTP_Get_Manufacturername
+LIBMTP_Get_Modelname
+LIBMTP_Get_Serialnumber
+LIBMTP_Get_Deviceversion
+LIBMTP_Get_Friendlyname
+LIBMTP_Set_Friendlyname
+LIBMTP_Get_Syncpartner
+LIBMTP_Set_Syncpartner
+LIBMTP_Get_Batterylevel
+LIBMTP_Get_Secure_Time
+LIBMTP_Get_Device_Certificate
+LIBMTP_Get_Supported_Filetypes
+LIBMTP_Get_Errorstack
+LIBMTP_Clear_Errorstack
+LIBMTP_Dump_Errorstack
+LIBMTP_Get_Storage
+LIBMTP_Format_Storage
+LIBMTP_new_file_t
+LIBMTP_destroy_file_t
+LIBMTP_Get_Filetype_Description
+LIBMTP_Get_Filelisting
+LIBMTP_Get_Filelisting_With_Callback
+LIBMTP_Get_Filemetadata
+LIBMTP_Get_File_To_File
+LIBMTP_Get_File_To_File_Descriptor
+LIBMTP_Send_File_From_File
+LIBMTP_Send_File_From_File_Descriptor
+LIBMTP_new_filesampledata_t
+LIBMTP_destroy_filesampledata_t
+LIBMTP_Get_Representative_Sample_Format
+LIBMTP_Send_Representative_Sample
+LIBMTP_Get_Representative_Sample
+LIBMTP_new_track_t
+LIBMTP_destroy_track_t
+LIBMTP_Get_Tracklisting
+LIBMTP_Get_Tracklisting_With_Callback
+LIBMTP_Get_Trackmetadata
+LIBMTP_Get_Track_To_File
+LIBMTP_Get_Track_To_File_Descriptor
+LIBMTP_Send_Track_From_File
+LIBMTP_Send_Track_From_File_Descriptor
+LIBMTP_Update_Track_Metadata
+LIBMTP_Track_Exists
+LIBMTP_new_folder_t
+LIBMTP_destroy_folder_t
+LIBMTP_Get_Folder_List
+LIBMTP_Find_Folder
+LIBMTP_Create_Folder
+LIBMTP_new_playlist_t
+LIBMTP_destroy_playlist_t
+LIBMTP_Get_Playlist_List
+LIBMTP_Get_Playlist
+LIBMTP_Create_New_Playlist
+LIBMTP_Update_Playlist
+LIBMTP_new_album_t
+LIBMTP_destroy_album_t
+LIBMTP_Get_Album_List
+LIBMTP_Get_Album
+LIBMTP_Create_New_Album
+LIBMTP_Update_Album
+LIBMTP_Delete_Object
+LIBMTP_Set_File_Name
+LIBMTP_Set_Folder_Name
+LIBMTP_Set_Track_Name
+LIBMTP_Set_Playlist_Name
+LIBMTP_Set_Album_Name
+LIBMTP_Set_Object_Filename
diff --git a/src/libptp-stdint.h b/src/libptp-stdint.h
new file mode 100644
index 0000000..80ecf41
--- /dev/null
+++ b/src/libptp-stdint.h
@@ -0,0 +1,2 @@
+/* This file is generated automatically by configure */
+#include <stdint.h>
diff --git a/src/libusb-glue.c b/src/libusb-glue.c
new file mode 100644
index 0000000..6c0effe
--- /dev/null
+++ b/src/libusb-glue.c
@@ -0,0 +1,1911 @@
+/*
+ * \file libusb-glue.c
+ * Low-level USB interface glue towards libusb.
+ *
+ * Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2005-2008 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2006-2007 Marcus Meissner
+ * Copyright (C) 2007 Ted Bullock
+ * Copyright (C) 2008 Chris Bagwell <chris@cnpbagwell.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Created by Richard Low on 24/12/2005. (as mtp-utils.c)
+ * Modified by Linus Walleij 2006-03-06
+ *  (Notice that Anglo-Saxons use little-endian dates and Swedes
+ *   use big-endian dates.)
+ *
+ */
+#include "config.h"
+#include "libmtp.h"
+#include "libusb-glue.h"
+#include "device-flags.h"
+#include "util.h"
+#include "ptp.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <usb.h>
+
+#include "ptp-pack.c"
+
+/* Aha, older libusb does not have USB_CLASS_PTP */
+#ifndef USB_CLASS_PTP
+#define USB_CLASS_PTP 6
+#endif
+
+/* To enable debug prints for USB stuff, switch on this */
+//#define ENABLE_USB_BULK_DEBUG
+
+/* Default USB timeout length.  This can be overridden as needed
+ * but should start with a reasonable value so most common 
+ * requests can be completed.  The original value of 4000 was
+ * not long enough for large file transfer.  Also, players can
+ * spend a bit of time collecting data.  Higher values also
+ * make connecting/disconnecting more reliable.
+ */
+#define USB_TIMEOUT_DEFAULT     10000
+
+/* USB control message data phase direction */
+#ifndef USB_DP_HTD
+#define USB_DP_HTD		(0x00 << 7)	/* host to device */
+#endif
+#ifndef USB_DP_DTH
+#define USB_DP_DTH		(0x01 << 7)	/* device to host */
+#endif
+
+/* USB Feature selector HALT */
+#ifndef USB_FEATURE_HALT
+#define USB_FEATURE_HALT	0x00
+#endif
+
+/* Internal data types */
+struct mtpdevice_list_struct {
+  struct usb_device *libusb_device;
+  PTPParams *params;
+  PTP_USB *ptp_usb;
+  uint32_t bus_location;
+  struct mtpdevice_list_struct *next;
+};
+typedef struct mtpdevice_list_struct mtpdevice_list_t;
+
+static const LIBMTP_device_entry_t mtp_device_table[] = {
+/* We include an .h file which is shared between us and libgphoto2 */
+#include "music-players.h"
+};
+static const int mtp_device_table_size = sizeof(mtp_device_table) / sizeof(LIBMTP_device_entry_t);
+
+// Local functions
+static struct usb_bus* init_usb();
+static void close_usb(PTP_USB* ptp_usb);
+static void find_interface_and_endpoints(struct usb_device *dev,
+					 uint8_t *interface,
+					 int* inep, 
+					 int* inep_maxpacket, 
+					 int* outep, 
+					 int* outep_maxpacket, 
+					 int* intep);
+static void clear_stall(PTP_USB* ptp_usb);
+static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev);
+static short ptp_write_func (unsigned long,PTPDataHandler*,void *data,unsigned long*);
+static short ptp_read_func (unsigned long,PTPDataHandler*,void *data,unsigned long*,int);
+static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep);
+static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status);
+
+/**
+ * Get a list of the supported USB devices.
+ *
+ * The developers depend on users of this library to constantly
+ * add in to the list of supported devices. What we need is the
+ * device name, USB Vendor ID (VID) and USB Product ID (PID).
+ * put this into a bug ticket at the project homepage, please.
+ * The VID/PID is used to let e.g. udev lift the device to
+ * console userspace access when it's plugged in.
+ *
+ * @param devices a pointer to a pointer that will hold a device
+ *        list after the call to this function, if it was
+ *        successful.
+ * @param numdevs a pointer to an integer that will hold the number
+ *        of devices in the device list if the call was successful.
+ * @return 0 if the list was successfull retrieved, any other
+ *        value means failure.
+ */
+int LIBMTP_Get_Supported_Devices_List(LIBMTP_device_entry_t ** const devices, int * const numdevs)
+{
+  *devices = (LIBMTP_device_entry_t *) &mtp_device_table;
+  *numdevs = mtp_device_table_size;
+  return 0;
+}
+
+
+static struct usb_bus* init_usb()
+{
+  usb_init();
+  usb_find_busses();
+  usb_find_devices();
+  return (usb_get_busses());
+}
+
+/**
+ * Small recursive function to append a new usb_device to the linked list of
+ * USB MTP devices
+ * @param devlist dynamic linked list of pointers to usb devices with MTP 
+ *        properties, to be extended with new device.
+ * @param newdevice the new device to add.
+ * @param bus_location bus for this device.
+ * @return an extended array or NULL on failure.
+ */
+static mtpdevice_list_t *append_to_mtpdevice_list(mtpdevice_list_t *devlist,
+						  struct usb_device *newdevice,
+						  uint32_t bus_location)
+{
+  mtpdevice_list_t *new_list_entry;
+  
+  new_list_entry = (mtpdevice_list_t *) malloc(sizeof(mtpdevice_list_t));
+  if (new_list_entry == NULL) {
+    return NULL;
+  }
+  // Fill in USB device, if we *HAVE* to make a copy of the device do it here.
+  new_list_entry->libusb_device = newdevice;
+  new_list_entry->bus_location = bus_location;
+  new_list_entry->next = NULL;
+  
+  if (devlist == NULL) {
+    return new_list_entry;
+  } else {
+    mtpdevice_list_t *tmp = devlist;
+    while (tmp->next != NULL) {
+      tmp = tmp->next;
+    }
+    tmp->next = new_list_entry;
+  }
+  return devlist;
+}
+
+/**
+ * Small recursive function to free dynamic memory allocated to the linked list
+ * of USB MTP devices
+ * @param devlist dynamic linked list of pointers to usb devices with MTP 
+ * properties.
+ * @return nothing
+ */
+static void free_mtpdevice_list(mtpdevice_list_t *devlist)
+{
+  mtpdevice_list_t *tmplist = devlist;
+
+  if (devlist == NULL)
+    return;
+  while (tmplist != NULL) {
+    mtpdevice_list_t *tmp = tmplist;
+    tmplist = tmplist->next;
+    // Do not free() the fields (ptp_usb, params)! These are used elsewhere.
+    free(tmp);
+  }
+  return;
+}
+
+/* Comment out this define to enable the original, more aggressive probing. */
+#define MILD_MTP_PROBING
+
+#ifdef MILD_MTP_PROBING
+/**
+ * This checks if a device has an interface with MTP description.
+ *
+ * @param dev a device struct from libusb.
+ * @param dumpfile set to non-NULL to make the descriptors dump out
+ *        to this file in human-readable hex so we can scruitinze them.
+ * @return 1 if the device is MTP compliant, 0 if not.
+ */
+static int probe_device_descriptor(struct usb_device *dev, FILE *dumpfile)
+{
+  usb_dev_handle *devh;
+  unsigned char buf[1024];
+  int i;
+  int ret;
+
+  /*
+   * Don't examine devices that are not likely to
+   * contain any MTP interface, update this the day
+   * you find some weird combination...
+   */
+  if (!(dev->descriptor.bDeviceClass == USB_CLASS_PER_INTERFACE ||
+	dev->descriptor.bDeviceClass == USB_CLASS_COMM ||
+	dev->descriptor.bDeviceClass == USB_CLASS_PTP ||
+	dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC)) {
+    return 0;
+  }
+
+  /* Attempt to open Device on this port */
+  devh = usb_open(dev);
+  if (devh == NULL) {
+    /* Could not open this device */
+    return 0;
+  }
+
+  /*
+   * This sometimes crashes on the j for loop below
+   * I think it is because config is NULL yet
+   * dev->descriptor.bNumConfigurations > 0
+   * this check should stop this
+   */
+  if (dev->config) {
+    /*
+     * Loop over the interfaces, and check for string "MTP"
+     * in the descriptions.
+     */
+
+    for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+      uint8_t j;
+
+      for (j = 0; j < dev->config[i].bNumInterfaces; j++) {
+        int k;
+        for (k = 0; k < dev->config[i].interface[j].num_altsetting; k++) {
+	  /* Current interface descriptor */
+	  struct usb_interface_descriptor *intf =
+	    &dev->config[i].interface[j].altsetting[k];
+
+          buf[0] = '\0';
+          ret = usb_get_string_simple(devh,
+				      dev->config[i].interface[j].altsetting[k].iInterface,
+				      (char *) buf,
+				      1024);
+
+	  if (ret < 3)
+	    continue;
+          if (strcmp((char *) buf, "MTP") == 0) {
+	    if (dumpfile != NULL) {
+              fprintf(dumpfile, "Configuration %d, interface %d, altsetting %d:\n", i, j, k);
+	      fprintf(dumpfile, "   Interface description contains the string \"MTP\"\n");
+	      fprintf(dumpfile, "   Device recognized as MTP, no further probing.\n");
+	    }
+            usb_close(devh);
+            return 1;
+          }
+       }
+      }
+    }
+  }
+
+  return 0;
+}
+
+#else /* MILD_MTP_PROBING */
+/**
+ * This checks if a device has an MTP descriptor. The descriptor was
+ * elaborated about in gPhoto bug 1482084, and some official documentation
+ * with no strings attached was published by Microsoft at
+ * http://www.microsoft.com/whdc/system/bus/USB/USBFAQ_intermed.mspx#E3HAC
+ *
+ * @param dev a device struct from libusb.
+ * @param dumpfile set to non-NULL to make the descriptors dump out
+ *        to this file in human-readable hex so we can scruitinze them.
+ * @return 1 if the device is MTP compliant, 0 if not.
+ */
+static int probe_device_descriptor(struct usb_device *dev, FILE *dumpfile)
+{
+  usb_dev_handle *devh;
+  unsigned char buf[1024], cmd;
+  int i;
+  int ret;
+  
+  /* Don't examine hubs (no point in that) */
+  if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) {
+    return 0;
+  }
+  
+  /* Attempt to open Device on this port */
+  devh = usb_open(dev);
+  if (devh == NULL) {
+    /* Could not open this device */
+    return 0;
+  }
+
+  /*
+   * This sometimes crashes on the j for loop below
+   * I think it is because config is NULL yet
+   * dev->descriptor.bNumConfigurations > 0
+   * this check should stop this
+   */
+  if (dev->config) {
+    /*
+     * Loop over the device configurations and interfaces. Nokia MTP-capable
+     * handsets (possibly others) typically have the string "MTP" in their
+     * MTP interface descriptions, that's how they can be detected, before
+     * we try the more esoteric "OS descriptors" (below).
+     */
+    for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+      uint8_t j;
+
+      for (j = 0; j < dev->config[i].bNumInterfaces; j++) {
+        int k;
+        for (k = 0; k < dev->config[i].interface[j].num_altsetting; k++) {
+	  /* Current interface descriptor */
+	  struct usb_interface_descriptor *intf =
+	    &dev->config[i].interface[j].altsetting[k];
+
+
+          buf[0] = '\0';
+          ret = usb_get_string_simple(devh,
+				      dev->config[i].interface[j].altsetting[k].iInterface,
+				      (char *) buf,
+				      1024);
+	  if (ret < 3)
+	    continue;
+          if (strcmp((char *) buf, "MTP") == 0) {
+	    if (dumpfile != NULL) {
+              fprintf(dumpfile, "Configuration %d, interface %d, altsetting %d:\n", i, j, k);
+	      fprintf(dumpfile, "   Interface description contains the string \"MTP\"\n");
+	      fprintf(dumpfile, "   Device recognized as MTP, no further probing.\n");
+	    }
+            usb_close(devh);
+            return 1;
+          }
+  #ifdef LIBUSB_HAS_GET_DRIVER_NP
+	  {
+	    /*
+	     * Specifically avoid probing anything else than USB mass storage devices
+	     * and non-associated drivers in Linux.
+	     */
+	    char devname[0x10];
+
+	    devname[0] = '\0';
+	    ret = usb_get_driver_np(devh,
+				    dev->config[i].interface[j].altsetting[k].iInterface,
+				    devname,
+				    sizeof(devname));
+	    if (devname[0] != '\0' && strcmp(devname, "usb-storage")) {
+	      printf("avoid probing device using kernel interface \"%s\"\n", devname);
+	      return 0;
+	    }
+	  }
+  #endif
+        }
+      }
+    }
+  } else {
+    if (dev->descriptor.bNumConfigurations)
+      printf("dev->config is NULL in probe_device_descriptor yet dev->descriptor.bNumConfigurations > 0\n");
+  }
+  
+  /* Read the special descriptor */
+  ret = usb_get_descriptor(devh, 0x03, 0xee, buf, sizeof(buf));
+
+  // Dump it, if requested
+  if (dumpfile != NULL && ret > 0) {
+    fprintf(dumpfile, "Microsoft device descriptor 0xee:\n");
+    data_dump_ascii(dumpfile, buf, ret, 16);
+  }
+  
+  /* Check if descriptor length is at least 10 bytes */
+  if (ret < 10) {
+    usb_close(devh);
+    return 0;
+  }
+      
+  /* Check if this device has a Microsoft Descriptor */
+  if (!((buf[2] == 'M') && (buf[4] == 'S') &&
+	(buf[6] == 'F') && (buf[8] == 'T'))) {
+    usb_close(devh);
+    return 0;
+  }
+      
+  /* Check if device responds to control message 1 or if there is an error */
+  cmd = buf[16];
+  ret = usb_control_msg (devh,
+			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
+			 cmd,
+			 0,
+			 4,
+			 (char *) buf,
+			 sizeof(buf),
+                         USB_TIMEOUT_DEFAULT);
+
+  // Dump it, if requested
+  if (dumpfile != NULL && ret > 0) {
+    fprintf(dumpfile, "Microsoft device response to control message 1, CMD 0x%02x:\n", cmd);
+    data_dump_ascii(dumpfile, buf, ret, 16);
+  }
+  
+  /* If this is true, the device either isn't MTP or there was an error */
+  if (ret <= 0x15) {
+    /* TODO: If there was an error, flag it and let the user know somehow */
+    /* if(ret == -1) {} */
+    usb_close(devh);
+    return 0;
+  }
+  
+  /* Check if device is MTP or if it is something like a USB Mass Storage 
+     device with Janus DRM support */
+  if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
+    usb_close(devh);
+    return 0;
+  }
+      
+  /* After this point we are probably dealing with an MTP device */
+
+  /* Check if device responds to control message 2 or if there is an error*/
+  ret = usb_control_msg (devh,
+			 USB_ENDPOINT_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR,
+			 cmd,
+			 0,
+			 5,
+			 (char *) buf,
+			 sizeof(buf),
+                         USB_TIMEOUT_DEFAULT);
+
+  // Dump it, if requested
+  if (dumpfile != NULL && ret > 0) {
+    fprintf(dumpfile, "Microsoft device response to control message 2, CMD 0x%02x:\n", cmd);
+    data_dump_ascii(dumpfile, buf, ret, 16);
+  }
+  
+  /* If this is true, the device errored against control message 2 */
+  if (ret == -1) {
+    /* TODO: Implement callback function to let managing program know there
+       was a problem, along with description of the problem */
+    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
+	    "ProductID:%04x encountered an error responding to "
+	    "control message 2.\n"
+	    "Problems may arrise but continuing\n",
+	    dev->descriptor.idVendor, dev->descriptor.idProduct);
+  } else if (ret <= 0x15) {
+    /* TODO: Implement callback function to let managing program know there
+       was a problem, along with description of the problem */
+    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
+	    "ProductID:%04x responded to control message 2 with a "
+	    "response that was too short. Problems may arrise but "
+	    "continuing\n",
+	    dev->descriptor.idVendor, dev->descriptor.idProduct);
+  } else if ((buf[0x12] != 'M') || (buf[0x13] != 'T') || (buf[0x14] != 'P')) {
+    /* TODO: Implement callback function to let managing program know there
+       was a problem, along with description of the problem */
+    fprintf(stderr, "Potential MTP Device with VendorID:%04x and "
+	    "ProductID:%04x encountered an error responding to "
+	    "control message 2\n"
+	    "Problems may arrise but continuing\n",
+	    dev->descriptor.idVendor, dev->descriptor.idProduct);
+  }
+  
+  /* Close the USB device handle */
+  usb_close(devh);
+  return 1;
+}
+#endif /* MILD_MTP_PROBING */
+
+/**
+ * This function scans through the connected usb devices on a machine and
+ * if they match known Vendor and Product identifiers appends them to the
+ * dynamic array mtp_device_list. Be sure to call 
+ * <code>free_mtpdevice_list(mtp_device_list)</code> when you are done 
+ * with it, assuming it is not NULL.
+ * @param mtp_device_list dynamic array of pointers to usb devices with MTP 
+ *        properties (if this list is not empty, new entries will be appended
+ *        to the list).
+ * @return LIBMTP_ERROR_NONE implies that devices have been found, scan the list
+ *        appropriately. LIBMTP_ERROR_NO_DEVICE_ATTACHED implies that no 
+ *        devices have been found.
+ */
+static LIBMTP_error_number_t get_mtp_usb_device_list(mtpdevice_list_t ** mtp_device_list)
+{
+  struct usb_bus *bus = init_usb();
+  for (; bus != NULL; bus = bus->next) {
+    struct usb_device *dev = bus->devices;
+    for (; dev != NULL; dev = dev->next) {
+      if (dev->descriptor.bDeviceClass != USB_CLASS_HUB) {
+	int i;
+        int found = 0;
+
+	// First check if we know about the device already.
+	// Devices well known to us will not have their descriptors
+	// probed, it caused problems with some devices.
+        for(i = 0; i < mtp_device_table_size; i++) {
+          if(dev->descriptor.idVendor == mtp_device_table[i].vendor_id &&
+            dev->descriptor.idProduct == mtp_device_table[i].product_id) {
+            /* Append this usb device to the MTP device list */
+            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, 
+							dev, 
+							bus->location);
+            found = 1;
+            break;
+          }
+        }
+	// If we didn't know it, try probing the "OS Descriptor".
+        if (!found) {
+          if (probe_device_descriptor(dev, NULL)) {
+            /* Append this usb device to the MTP USB Device List */
+            *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, 
+							dev,
+							bus->location);
+          }
+          /*
+	   * By thomas_-_s: Also append devices that are no MTP but PTP devices
+	   * if this is commented out.
+	   */
+	  /*
+	  else {
+	    // Check whether the device is no USB hub but a PTP.
+	    if ( dev->config != NULL &&dev->config->interface->altsetting->bInterfaceClass == USB_CLASS_PTP && dev->descriptor.bDeviceClass != USB_CLASS_HUB ) {
+	      *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev, bus->location);
+	    }
+          }
+	  */
+        }
+      }
+    }
+  }
+  
+  /* If nothing was found we end up here. */
+  if(*mtp_device_list == NULL) {
+    return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
+  }
+  return LIBMTP_ERROR_NONE;
+}
+
+/**
+ * Detect the raw MTP device descriptors and return a list of
+ * of the devices found.
+ * 
+ * @param devices a pointer to a variable that will hold
+ *        the list of raw devices found. This may be NULL
+ *        on return if the number of detected devices is zero.
+ *        The user shall simply <code>free()</code> this
+ *        variable when finished with the raw devices,
+ *        in order to release memory.
+ * @param numdevs a pointer to an integer that will hold 
+ *        the number of devices in the list. This may
+ *        be 0.
+ * @return 0 if successful, any other value means failure.
+ */
+LIBMTP_error_number_t LIBMTP_Detect_Raw_Devices(LIBMTP_raw_device_t ** devices, 
+			      int * numdevs)
+{
+  mtpdevice_list_t *devlist = NULL;
+  mtpdevice_list_t *dev;
+  LIBMTP_error_number_t ret;
+  LIBMTP_raw_device_t *retdevs;
+  int devs = 0;
+  int i, j;
+
+  ret = get_mtp_usb_device_list(&devlist);
+  if (ret == LIBMTP_ERROR_NO_DEVICE_ATTACHED) {
+    *devices = NULL;
+    *numdevs = 0;
+    return ret;
+  } else if (ret != LIBMTP_ERROR_NONE) {
+    fprintf(stderr, "LIBMTP PANIC: get_mtp_usb_device_list() "
+	    "error code: %d on line %d\n", ret, __LINE__);
+    return ret;
+  }
+
+  // Get list size
+  dev = devlist;
+  while (dev != NULL) {
+    devs++;
+    dev = dev->next;
+  }
+  if (devs == 0) {
+    *devices = NULL;
+    *numdevs = 0;
+    return LIBMTP_ERROR_NONE;
+  }
+  // Conjure a device list
+  retdevs = (LIBMTP_raw_device_t *) malloc(sizeof(LIBMTP_raw_device_t) * devs);
+  if (retdevs == NULL) {
+    // Out of memory
+    *devices = NULL;
+    *numdevs = 0;
+    return LIBMTP_ERROR_MEMORY_ALLOCATION;
+  }
+  dev = devlist;
+  i = 0;
+  while (dev != NULL) {
+    int device_known = 0;
+
+    // Assign default device info
+    retdevs[i].device_entry.vendor = NULL;
+    retdevs[i].device_entry.vendor_id = dev->libusb_device->descriptor.idVendor;
+    retdevs[i].device_entry.product = NULL;
+    retdevs[i].device_entry.product_id = dev->libusb_device->descriptor.idProduct;
+    retdevs[i].device_entry.device_flags = 0x00000000U;
+    // See if we can locate some additional vendor info and device flags
+    for(j = 0; j < mtp_device_table_size; j++) {
+      if(dev->libusb_device->descriptor.idVendor == mtp_device_table[j].vendor_id &&
+	 dev->libusb_device->descriptor.idProduct == mtp_device_table[j].product_id) {
+	device_known = 1;
+	retdevs[i].device_entry.vendor = mtp_device_table[j].vendor;
+	retdevs[i].device_entry.product = mtp_device_table[j].product;
+	retdevs[i].device_entry.device_flags = mtp_device_table[j].device_flags;
+
+#ifdef _AFT_BUILD
+    // Disable the following features for all devices.
+	retdevs[i].device_entry.device_flags |= DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST|
+                                            DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST|
+                                            DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST;
+#endif
+
+#ifdef ENABLE_USB_BULK_DEBUG
+	// This device is known to the developers
+	fprintf(stderr, "Device %d (VID=%04x and PID=%04x) is a %s %s.\n", 
+		i,
+		dev->libusb_device->descriptor.idVendor,
+		dev->libusb_device->descriptor.idProduct,
+		mtp_device_table[j].vendor,
+		mtp_device_table[j].product);
+#endif
+	break;
+      }
+    }
+    if (!device_known) {
+      // This device is unknown to the developers
+      fprintf(stderr, "Device %d (VID=%04x and PID=%04x) is UNKNOWN.\n", 
+	      i,
+	      dev->libusb_device->descriptor.idVendor,
+	      dev->libusb_device->descriptor.idProduct);
+      fprintf(stderr, "Please report this VID/PID and the device model to the "
+	      "libmtp development team\n");
+      /*
+       * Trying to get iManufacturer or iProduct from the device at this
+       * point would require opening a device handle, that we don't want
+       * to do right now. (Takes time for no good enough reason.)
+       */
+    }
+    // Save the location on the bus
+    retdevs[i].bus_location = dev->bus_location;
+    retdevs[i].devnum = dev->libusb_device->devnum;
+    i++;
+    dev = dev->next;
+  }  
+  *devices = retdevs;
+  *numdevs = i;
+  free_mtpdevice_list(devlist);
+  return LIBMTP_ERROR_NONE;
+}
+
+/**
+ * This routine just dumps out low-level
+ * USB information about the current device.
+ * @param ptp_usb the USB device to get information from.
+ */
+void dump_usbinfo(PTP_USB *ptp_usb)
+{
+  struct usb_device *dev;
+
+#ifdef LIBUSB_HAS_GET_DRIVER_NP
+  char devname[0x10];
+  int res;
+  
+  devname[0] = '\0';
+  res = usb_get_driver_np(ptp_usb->handle, (int) ptp_usb->interface, devname, sizeof(devname));
+  if (devname[0] != '\0') {
+    printf("   Using kernel interface \"%s\"\n", devname);
+  }
+#endif
+  dev = usb_device(ptp_usb->handle);
+  printf("   bcdUSB: %d\n", dev->descriptor.bcdUSB);
+  printf("   bDeviceClass: %d\n", dev->descriptor.bDeviceClass);
+  printf("   bDeviceSubClass: %d\n", dev->descriptor.bDeviceSubClass);
+  printf("   bDeviceProtocol: %d\n", dev->descriptor.bDeviceProtocol);
+  printf("   idVendor: %04x\n", dev->descriptor.idVendor);
+  printf("   idProduct: %04x\n", dev->descriptor.idProduct);
+  printf("   IN endpoint maxpacket: %d bytes\n", ptp_usb->inep_maxpacket);
+  printf("   OUT endpoint maxpacket: %d bytes\n", ptp_usb->outep_maxpacket);
+  printf("   Raw device info:\n");
+  printf("      Bus location: %d\n", ptp_usb->rawdevice.bus_location);
+  printf("      Device number: %d\n", ptp_usb->rawdevice.devnum);
+  printf("      Device entry info:\n");
+  printf("         Vendor: %s\n", ptp_usb->rawdevice.device_entry.vendor);
+  printf("         Vendor id: 0x%04x\n", ptp_usb->rawdevice.device_entry.vendor_id);
+  printf("         Product: %s\n", ptp_usb->rawdevice.device_entry.product);
+  printf("         Vendor id: 0x%04x\n", ptp_usb->rawdevice.device_entry.product_id);
+  printf("         Device flags: 0x%08x\n", ptp_usb->rawdevice.device_entry.device_flags);
+  (void) probe_device_descriptor(dev, stdout);
+}
+
+/**
+ * Retrieve the apropriate playlist extension for this
+ * device. Rather hacky at the moment. This is probably
+ * desired by the managing software, but when creating
+ * lists on the device itself you notice certain preferences.
+ * @param ptp_usb the USB device to get suggestion for.
+ * @return the suggested playlist extension.
+ */
+const char *get_playlist_extension(PTP_USB *ptp_usb)
+{
+  struct usb_device *dev;
+  static char creative_pl_extension[] = ".zpl";
+  static char default_pl_extension[] = ".pla";
+
+  dev = usb_device(ptp_usb->handle);
+  if (dev->descriptor.idVendor == 0x041e) {
+    return creative_pl_extension;
+  }
+  return default_pl_extension;
+}
+
+static void
+libusb_glue_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);
+}  
+
+static void
+libusb_glue_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);
+}
+
+
+/*
+ * ptp_read_func() and ptp_write_func() are
+ * based on same functions usb.c in libgphoto2.
+ * Much reading packet logs and having fun with trials and errors
+ * reveals that WMP / Windows is probably using an algorithm like this
+ * for large transfers:
+ *
+ * 1. Send the command (0x0c bytes) if headers are split, else, send 
+ *    command plus sizeof(endpoint) - 0x0c bytes.
+ * 2. Send first packet, max size to be sizeof(endpoint) but only when using
+ *    split headers. Else goto 3.
+ * 3. REPEAT send 0x10000 byte chunks UNTIL remaining bytes < 0x10000
+ *    We call 0x10000 CONTEXT_BLOCK_SIZE.
+ * 4. Send remaining bytes MOD sizeof(endpoint)
+ * 5. Send remaining bytes. If this happens to be exactly sizeof(endpoint)
+ *    then also send a zero-length package.
+ *
+ * Further there is some special quirks to handle zero reads from the
+ * device, since some devices can't do them at all due to shortcomings
+ * of the USB slave controller in the device.
+ */
+#define CONTEXT_BLOCK_SIZE_1	0x3e00
+#define CONTEXT_BLOCK_SIZE_2  0x200
+#define CONTEXT_BLOCK_SIZE    CONTEXT_BLOCK_SIZE_1+CONTEXT_BLOCK_SIZE_2
+static short
+ptp_read_func (
+	unsigned long size, PTPDataHandler *handler,void *data,
+	unsigned long *readbytes,
+	int readzero
+) {
+  PTP_USB *ptp_usb = (PTP_USB *)data;
+  unsigned long toread = 0;
+  int result = 0;
+  unsigned long curread = 0;
+  unsigned long written;
+  unsigned char *bytes;
+  int expect_terminator_byte = 0;
+
+  // This is the largest block we'll need to read in.
+  bytes = malloc(CONTEXT_BLOCK_SIZE);
+  while (curread < size) {
+    
+#ifdef ENABLE_USB_BULK_DEBUG
+    printf("Remaining size to read: 0x%04lx bytes\n", size - curread);
+#endif
+    // check equal to condition here
+    if (size - curread < CONTEXT_BLOCK_SIZE)
+    {
+      // this is the last packet
+      toread = size - curread;
+      // this is equivalent to zero read for these devices
+      if (readzero && FLAG_NO_ZERO_READS(ptp_usb) && toread % 64 == 0) {
+        toread += 1;
+        expect_terminator_byte = 1;
+      }
+    }
+    else if (curread == 0)
+      // we are first packet, but not last packet
+      toread = CONTEXT_BLOCK_SIZE_1;
+    else if (toread == CONTEXT_BLOCK_SIZE_1)
+      toread = CONTEXT_BLOCK_SIZE_2;
+    else if (toread == CONTEXT_BLOCK_SIZE_2)
+      toread = CONTEXT_BLOCK_SIZE_1;
+    else
+      printf("unexpected toread size 0x%04x, 0x%04x remaining bytes\n", 
+	     (unsigned int) toread, (unsigned int) (size-curread));
+
+#ifdef ENABLE_USB_BULK_DEBUG
+    printf("Reading in 0x%04lx bytes\n", toread);
+#endif
+    result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, (char*)bytes, toread, ptp_usb->timeout);
+#ifdef ENABLE_USB_BULK_DEBUG
+    printf("Result of read: 0x%04x\n", result);
+#endif
+        
+    if (result < 0) {
+      return PTP_ERROR_IO;
+    }
+#ifdef ENABLE_USB_BULK_DEBUG
+    printf("<==USB IN\n");
+    if (result == 0)
+      printf("Zero Read\n");
+    else if (result < 0) 
+      fprintf(stderr, "USB_BULK_READ result=%#x\n", result);
+    else 
+      data_dump_ascii (stdout,bytes,result,16);
+#endif
+    
+    // want to discard extra byte
+    if (expect_terminator_byte && result == toread)
+    {
+#ifdef ENABLE_USB_BULK_DEBUG
+      printf("<==USB IN\nDiscarding extra byte\n");
+#endif
+      result--;
+    }
+    
+    int putfunc_ret = handler->putfunc(NULL, handler->priv, result, bytes, &written);
+    if (putfunc_ret != PTP_RC_OK)
+      return putfunc_ret;
+    
+    ptp_usb->current_transfer_complete += result;
+    curread += result;
+
+    // Increase counters, call callback
+    if (ptp_usb->callback_active) {
+      if (ptp_usb->current_transfer_complete >= ptp_usb->current_transfer_total) {
+	// send last update and disable callback.
+	ptp_usb->current_transfer_complete = ptp_usb->current_transfer_total;
+	ptp_usb->callback_active = 0;
+      }
+      if (ptp_usb->current_transfer_callback != NULL) {
+	int ret;
+	ret = ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete,
+						 ptp_usb->current_transfer_total,
+						 ptp_usb->current_transfer_callback_data);
+	if (ret != 0) {
+	  return PTP_ERROR_CANCEL;
+	}
+      }
+    }  
+
+    if (result < toread) /* short reads are common */
+      break;
+  }
+  if (readbytes) *readbytes = curread;
+  free (bytes);
+  
+  // there might be a zero packet waiting for us...
+  if (readzero && 
+      !FLAG_NO_ZERO_READS(ptp_usb) && 
+      curread % ptp_usb->outep_maxpacket == 0) {
+    char temp;
+    int zeroresult = 0;
+
+#ifdef ENABLE_USB_BULK_DEBUG
+    printf("<==USB IN\n");
+    printf("Zero Read\n");
+#endif
+    zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &temp, 0, ptp_usb->timeout);
+    if (zeroresult != 0)
+      printf("LIBMTP panic: unable to read in zero packet, response 0x%04x", zeroresult);
+  }
+  
+  return PTP_RC_OK;
+}
+
+static short
+ptp_write_func (
+        unsigned long   size,
+        PTPDataHandler  *handler,
+        void            *data,
+        unsigned long   *written
+) {
+  PTP_USB *ptp_usb = (PTP_USB *)data;
+  unsigned long towrite = 0;
+  int result = 0;
+  unsigned long curwrite = 0;
+  unsigned char *bytes;
+
+  // This is the largest block we'll need to read in.  
+  bytes = malloc(CONTEXT_BLOCK_SIZE);
+  if (!bytes) {
+    return PTP_ERROR_IO;
+  }
+  while (curwrite < size) {
+    unsigned long usbwritten = 0;
+    towrite = size-curwrite;
+    if (towrite > CONTEXT_BLOCK_SIZE) {
+      towrite = CONTEXT_BLOCK_SIZE;
+    } else {
+      // This magic makes packets the same size that WMP send them.
+      if (towrite > ptp_usb->outep_maxpacket && towrite % ptp_usb->outep_maxpacket != 0) {
+        towrite -= towrite % ptp_usb->outep_maxpacket;
+      }
+    }
+    int getfunc_ret = handler->getfunc(NULL, handler->priv,towrite,bytes,&towrite);
+    if (getfunc_ret != PTP_RC_OK)
+      return getfunc_ret;
+    while (usbwritten < towrite) {
+	    result = USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,((char*)bytes+usbwritten),towrite-usbwritten,ptp_usb->timeout);
+#ifdef ENABLE_USB_BULK_DEBUG
+	    printf("USB OUT==>\n");
+        if (result > 0) { 
+            data_dump_ascii (stdout,bytes+usbwritten,result,16);
+        } else {
+            fprintf(stderr, "USB_BULK_WRITE: result=%#x\n", result);
+        }
+#endif
+	    if (result < 0) {
+	      return PTP_ERROR_IO;
+	    }
+	    // check for result == 0 perhaps too.
+	    // Increase counters
+	    ptp_usb->current_transfer_complete += result;
+	    curwrite += result;
+	    usbwritten += result;
+    }
+    // call callback
+    if (ptp_usb->callback_active) {
+      if (ptp_usb->current_transfer_complete >= ptp_usb->current_transfer_total) {
+	// send last update and disable callback.
+	ptp_usb->current_transfer_complete = ptp_usb->current_transfer_total;
+	ptp_usb->callback_active = 0;
+      }
+      if (ptp_usb->current_transfer_callback != NULL) {
+	int ret;
+	ret = ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete,
+						 ptp_usb->current_transfer_total,
+						 ptp_usb->current_transfer_callback_data);
+	if (ret != 0) {
+	  return PTP_ERROR_CANCEL;
+	}
+      }
+    }
+    if (result < towrite) /* short writes happen */
+      break;
+  }
+  free (bytes);
+  if (written) {
+    *written = curwrite;
+  }
+  
+
+  // If this is the last transfer send a zero write if required
+  if (ptp_usb->current_transfer_complete >= ptp_usb->current_transfer_total) {
+    if ((towrite % ptp_usb->outep_maxpacket) == 0) {
+#ifdef ENABLE_USB_BULK_DEBUG
+      printf("USB OUT==>\n");
+      printf("Zero Write\n");
+#endif
+      result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)"x",0,ptp_usb->timeout);
+    }
+  }
+    
+  if (result < 0)
+    return PTP_ERROR_IO;
+  return PTP_RC_OK;
+}
+
+/* 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,
+	       unsigned long *putlen
+) {
+	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;
+
+	if (priv->curoff + sendlen > priv->size) {
+		priv->data = realloc (priv->data, priv->curoff+sendlen);
+		priv->size = priv->curoff + sendlen;
+	}
+	memcpy (priv->data + priv->curoff, data, sendlen);
+	priv->curoff += sendlen;
+	*putlen = 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));
+	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;
+}
+
+/* send / receive functions */
+
+uint16_t
+ptp_usb_sendreq (PTPParams* params, PTPContainer* req)
+{
+	uint16_t ret;
+	PTPUSBBulkContainer usbreq;
+	PTPDataHandler	memhandler;
+	unsigned long written = 0;
+	unsigned long towrite;
+#ifdef ENABLE_USB_BULK_DEBUG
+	char txt[256];
+
+	(void) ptp_render_opcode (params, req->Code, sizeof(txt), txt);
+	printf("REQUEST: 0x%04x, %s\n", req->Code, txt);
+#endif
+	/* build appropriate USB container */
+	usbreq.length=htod32(PTP_USB_BULK_REQ_LEN-
+		(sizeof(uint32_t)*(5-req->Nparam)));
+	usbreq.type=htod16(PTP_USB_CONTAINER_COMMAND);
+	usbreq.code=htod16(req->Code);
+	usbreq.trans_id=htod32(req->Transaction_ID);
+	usbreq.payload.params.param1=htod32(req->Param1);
+	usbreq.payload.params.param2=htod32(req->Param2);
+	usbreq.payload.params.param3=htod32(req->Param3);
+	usbreq.payload.params.param4=htod32(req->Param4);
+	usbreq.payload.params.param5=htod32(req->Param5);
+	/* send it to responder */
+	towrite = PTP_USB_BULK_REQ_LEN-(sizeof(uint32_t)*(5-req->Nparam));
+	ptp_init_send_memory_handler (&memhandler, (unsigned char*)&usbreq, towrite);
+	ret=ptp_write_func(
+		towrite,
+		&memhandler,
+		params->data,
+		&written
+	);
+	ptp_exit_send_memory_handler (&memhandler);
+	if (ret!=PTP_RC_OK && ret!=PTP_ERROR_CANCEL) {
+		ret = PTP_ERROR_IO;
+	}
+	if (written != towrite && ret != PTP_ERROR_CANCEL && ret != PTP_ERROR_IO) {
+		libusb_glue_error (params, 
+			"PTP: request code 0x%04x sending req wrote only %ld bytes instead of %d",
+			req->Code, written, towrite
+		);
+		ret = PTP_ERROR_IO;
+	}
+	return ret;
+}
+
+uint16_t
+ptp_usb_senddata (PTPParams* params, PTPContainer* ptp,
+		  unsigned long size, PTPDataHandler *handler
+) {
+	uint16_t ret;
+	int wlen, datawlen;
+	unsigned long written;
+	PTPUSBBulkContainer usbdata;
+	uint32_t bytes_left_to_transfer;
+	PTPDataHandler memhandler;
+
+#ifdef ENABLE_USB_BULK_DEBUG
+	printf("SEND DATA PHASE\n");
+#endif
+	/* build appropriate USB container */
+	usbdata.length	= htod32(PTP_USB_BULK_HDR_LEN+size);
+	usbdata.type	= htod16(PTP_USB_CONTAINER_DATA);
+	usbdata.code	= htod16(ptp->Code);
+	usbdata.trans_id= htod32(ptp->Transaction_ID);
+  
+	((PTP_USB*)params->data)->current_transfer_complete = 0;
+	((PTP_USB*)params->data)->current_transfer_total = size+PTP_USB_BULK_HDR_LEN;
+
+	if (params->split_header_data) {
+		datawlen = 0;
+		wlen = PTP_USB_BULK_HDR_LEN;
+	} else {
+		unsigned long gotlen;
+		/* For all camera devices. */
+		datawlen = (size<PTP_USB_BULK_PAYLOAD_LEN_WRITE)?size:PTP_USB_BULK_PAYLOAD_LEN_WRITE;
+		wlen = PTP_USB_BULK_HDR_LEN + datawlen;
+    
+		ret = handler->getfunc(params, handler->priv, datawlen, usbdata.payload.data, &gotlen);
+		if (ret != PTP_RC_OK)
+			return ret;
+		if (gotlen != datawlen)
+			return PTP_RC_GeneralError;
+	}
+	ptp_init_send_memory_handler (&memhandler, (unsigned char *)&usbdata, wlen);
+	/* send first part of data */
+	ret = ptp_write_func(wlen, &memhandler, params->data, &written);
+	ptp_exit_send_memory_handler (&memhandler);
+	if (ret!=PTP_RC_OK) {
+		return ret;
+	}
+	if (size <= datawlen) return ret;
+	/* if everything OK send the rest */
+	bytes_left_to_transfer = size-datawlen;
+	ret = PTP_RC_OK;
+	while(bytes_left_to_transfer > 0) {
+		ret = ptp_write_func (bytes_left_to_transfer, handler, params->data, &written);
+		if (ret != PTP_RC_OK)
+			break;
+		if (written == 0) {
+			ret = PTP_ERROR_IO;
+			break;
+		}
+		bytes_left_to_transfer -= written;
+	}
+	if (ret!=PTP_RC_OK && ret!=PTP_ERROR_CANCEL)
+		ret = PTP_ERROR_IO;
+	return ret;
+}
+
+static uint16_t ptp_usb_getpacket(PTPParams *params,
+		PTPUSBBulkContainer *packet, unsigned long *rlen)
+{
+	PTPDataHandler	memhandler;
+	uint16_t	ret;
+	unsigned char	*x = NULL;
+
+	/* read the header and potentially the first data */
+	if (params->response_packet_size > 0) {
+		/* If there is a buffered packet, just use it. */
+		memcpy(packet, params->response_packet, params->response_packet_size);
+		*rlen = params->response_packet_size;
+		free(params->response_packet);
+		params->response_packet = NULL;
+		params->response_packet_size = 0;
+		/* Here this signifies a "virtual read" */
+		return PTP_RC_OK;
+	}
+	ptp_init_recv_memory_handler (&memhandler);
+	ret = ptp_read_func(PTP_USB_BULK_HS_MAX_PACKET_LEN_READ, &memhandler, params->data, rlen, 0);
+	ptp_exit_recv_memory_handler (&memhandler, &x, rlen);
+	if (x) {
+		memcpy (packet, x, *rlen);
+		free (x);
+	}
+	return ret;
+}
+
+uint16_t
+ptp_usb_getdata (PTPParams* params, PTPContainer* ptp, PTPDataHandler *handler)
+{
+	uint16_t ret;
+	PTPUSBBulkContainer usbdata;
+	unsigned long	written;
+	PTP_USB *ptp_usb = (PTP_USB *) params->data;
+
+#ifdef ENABLE_USB_BULK_DEBUG
+	printf("GET DATA PHASE\n");
+#endif
+	memset(&usbdata,0,sizeof(usbdata));
+	do {
+		unsigned long len, rlen;
+
+		ret = ptp_usb_getpacket(params, &usbdata, &rlen);
+		if (ret!=PTP_RC_OK) {
+			ret = PTP_ERROR_IO;
+			break;
+		}
+		if (dtoh16(usbdata.type)!=PTP_USB_CONTAINER_DATA) {
+			ret = PTP_ERROR_DATA_EXPECTED;
+			break;
+		}
+		if (dtoh16(usbdata.code)!=ptp->Code) {
+			if (FLAG_IGNORE_HEADER_ERRORS(ptp_usb)) {
+				libusb_glue_debug (params, "ptp2/ptp_usb_getdata: detected a broken "
+					   "PTP header, code field insane, expect problems! (But continuing)");
+				// Repair the header, so it won't wreak more havoc, don't just ignore it.
+				// Typically these two fields will be broken.
+				usbdata.code	 = htod16(ptp->Code);
+				usbdata.trans_id = htod32(ptp->Transaction_ID);
+				ret = PTP_RC_OK;
+			} else {
+				ret = dtoh16(usbdata.code);
+				// This filters entirely insane garbage return codes, but still
+				// makes it possible to return error codes in the code field when
+				// getting data. It appears Windows ignores the contents of this 
+				// field entirely.
+				if (ret < PTP_RC_Undefined || ret > PTP_RC_SpecificationOfDestinationUnsupported) {
+					libusb_glue_debug (params, "ptp2/ptp_usb_getdata: detected a broken "
+						   "PTP header, code field insane.");
+					ret = PTP_ERROR_IO;
+				}
+				break;
+			}
+		}
+		if (usbdata.length == 0xffffffffU) {
+			/* Copy first part of data to 'data' */
+      int putfunc_ret = 
+			handler->putfunc(
+				params, handler->priv, rlen - PTP_USB_BULK_HDR_LEN, usbdata.payload.data,
+				&written
+			);
+      if (putfunc_ret != PTP_RC_OK)
+        return putfunc_ret;
+			/* stuff data directly to passed data handler */
+			while (1) {
+				unsigned long readdata;
+				uint16_t xret;
+
+				xret = ptp_read_func(
+					PTP_USB_BULK_HS_MAX_PACKET_LEN_READ,
+					handler,
+					params->data,
+					&readdata,
+					0
+				);
+				if (xret != PTP_RC_OK)
+					return xret;
+				if (readdata < PTP_USB_BULK_HS_MAX_PACKET_LEN_READ)
+					break;
+			}
+			return PTP_RC_OK;
+		}
+		if (rlen > dtoh32(usbdata.length)) {
+			/*
+			 * Buffer the surplus response packet if it is >=
+			 * PTP_USB_BULK_HDR_LEN
+			 * (i.e. it is probably an entire package)
+			 * else discard it as erroneous surplus data.
+			 * This will even work if more than 2 packets appear
+			 * in the same transaction, they will just be handled
+			 * iteratively.
+			 *
+			 * Marcus observed stray bytes on iRiver devices;
+			 * these are still discarded.
+			 */
+			unsigned int packlen = dtoh32(usbdata.length);
+			unsigned int surplen = rlen - packlen;
+
+			if (surplen >= PTP_USB_BULK_HDR_LEN) {
+				params->response_packet = malloc(surplen);
+				memcpy(params->response_packet,
+				       (uint8_t *) &usbdata + packlen, surplen);
+				params->response_packet_size = surplen;
+			/* Ignore reading one extra byte if device flags have been set */
+			} else if(!FLAG_NO_ZERO_READS(ptp_usb) &&
+				  (rlen - dtoh32(usbdata.length) == 1)) {
+			  libusb_glue_debug (params, "ptp2/ptp_usb_getdata: read %d bytes "
+				     "too much, expect problems!", 
+				     rlen - dtoh32(usbdata.length));
+			}
+			rlen = packlen;
+		}
+
+		/* For most PTP devices rlen is 512 == sizeof(usbdata)
+		 * here. For MTP devices splitting header and data it might
+		 * be 12.
+		 */
+		/* Evaluate full data length. */
+		len=dtoh32(usbdata.length)-PTP_USB_BULK_HDR_LEN;
+
+		/* autodetect split header/data MTP devices */
+		if (dtoh32(usbdata.length) > 12 && (rlen==12))
+			params->split_header_data = 1;
+
+		/* Copy first part of data to 'data' */
+    int putfunc_ret = 
+		handler->putfunc(
+			params, handler->priv, rlen - PTP_USB_BULK_HDR_LEN, usbdata.payload.data,
+			&written
+		);
+    if (putfunc_ret != PTP_RC_OK)
+      return putfunc_ret;
+
+		if (FLAG_NO_ZERO_READS(ptp_usb) &&
+		    len+PTP_USB_BULK_HDR_LEN == PTP_USB_BULK_HS_MAX_PACKET_LEN_READ) {
+#ifdef ENABLE_USB_BULK_DEBUG
+		  printf("Reading in extra terminating byte\n");
+#endif
+		  // need to read in extra byte and discard it
+		  int result = 0;
+		  char byte = 0;
+                  result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &byte, 1, ptp_usb->timeout);
+
+		  if (result != 1)
+		    printf("Could not read in extra byte for PTP_USB_BULK_HS_MAX_PACKET_LEN_READ long file, return value 0x%04x\n", result);
+		} else if (len+PTP_USB_BULK_HDR_LEN == PTP_USB_BULK_HS_MAX_PACKET_LEN_READ && params->split_header_data == 0) {
+		  int zeroresult = 0;
+		  char zerobyte = 0;
+
+#ifdef ENABLE_USB_BULK_DEBUG
+		  printf("Reading in zero packet after header\n");
+#endif
+                  zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &zerobyte, 0, ptp_usb->timeout);
+		  
+		  if (zeroresult != 0)
+		    printf("LIBMTP panic: unable to read in zero packet, response 0x%04x", zeroresult);
+		}
+		
+		/* Is that all of data? */
+		if (len+PTP_USB_BULK_HDR_LEN<=rlen) {
+		  break;
+		}
+		
+		ret = ptp_read_func(len - (rlen - PTP_USB_BULK_HDR_LEN),
+				    handler,
+				    params->data, &rlen, 1);
+		
+		if (ret!=PTP_RC_OK) {
+		  break;
+		}
+	} while (0);
+	return ret;
+}
+
+uint16_t
+ptp_usb_getresp (PTPParams* params, PTPContainer* resp)
+{
+	uint16_t ret;
+	unsigned long rlen;
+	PTPUSBBulkContainer usbresp;
+	PTP_USB *ptp_usb = (PTP_USB *)(params->data);
+
+#ifdef ENABLE_USB_BULK_DEBUG
+	printf("RESPONSE: ");
+#endif
+	memset(&usbresp,0,sizeof(usbresp));
+	/* read response, it should never be longer than sizeof(usbresp) */
+	ret = ptp_usb_getpacket(params, &usbresp, &rlen);
+
+	// Fix for bevahiour reported by Scott Snyder on Samsung YP-U3. The player
+	// sends a packet containing just zeroes of length 2 (up to 4 has been seen too)
+	// after a NULL packet when it should send the response. This code ignores
+	// such illegal packets.
+	while (ret==PTP_RC_OK && rlen<PTP_USB_BULK_HDR_LEN && usbresp.length==0) {
+	  libusb_glue_debug (params, "ptp_usb_getresp: detected short response "
+		     "of %d bytes, expect problems! (re-reading "
+		     "response), rlen");
+	  ret = ptp_usb_getpacket(params, &usbresp, &rlen);
+	}
+
+	if (ret!=PTP_RC_OK) {
+		ret = PTP_ERROR_IO;
+	} else
+	if (dtoh16(usbresp.type)!=PTP_USB_CONTAINER_RESPONSE) {
+		ret = PTP_ERROR_RESP_EXPECTED;
+	} else
+	if (dtoh16(usbresp.code)!=resp->Code) {
+		ret = dtoh16(usbresp.code);
+	}
+#ifdef ENABLE_USB_BULK_DEBUG
+	printf("%04x\n", ret);
+#endif
+	if (ret!=PTP_RC_OK) {
+/*		libusb_glue_error (params,
+		"PTP: request code 0x%04x getting resp error 0x%04x",
+			resp->Code, ret);*/
+		return ret;
+	}
+	/* build an appropriate PTPContainer */
+	resp->Code=dtoh16(usbresp.code);
+	resp->SessionID=params->session_id;
+	resp->Transaction_ID=dtoh32(usbresp.trans_id);
+	if (FLAG_IGNORE_HEADER_ERRORS(ptp_usb)) {
+		if (resp->Transaction_ID != params->transaction_id-1) {
+			libusb_glue_debug (params, "ptp_usb_getresp: detected a broken "
+				   "PTP header, transaction ID insane, expect "
+				   "problems! (But continuing)");
+			// Repair the header, so it won't wreak more havoc.
+			resp->Transaction_ID = params->transaction_id-1;
+		}
+	}
+	resp->Param1=dtoh32(usbresp.payload.params.param1);
+	resp->Param2=dtoh32(usbresp.payload.params.param2);
+	resp->Param3=dtoh32(usbresp.payload.params.param3);
+	resp->Param4=dtoh32(usbresp.payload.params.param4);
+	resp->Param5=dtoh32(usbresp.payload.params.param5);
+	return ret;
+}
+
+/* Event handling functions */
+
+/* PTP Events wait for or check mode */
+#define PTP_EVENT_CHECK			0x0000	/* waits for */
+#define PTP_EVENT_CHECK_FAST		0x0001	/* checks */
+
+static inline uint16_t
+ptp_usb_event (PTPParams* params, PTPContainer* event, int wait)
+{
+	uint16_t ret;
+	int result;
+	unsigned long rlen;
+	PTPUSBEventContainer usbevent;
+	PTP_USB *ptp_usb = (PTP_USB *)(params->data);
+
+	memset(&usbevent,0,sizeof(usbevent));
+
+	if ((params==NULL) || (event==NULL)) 
+		return PTP_ERROR_BADPARAM;
+	ret = PTP_RC_OK;
+	switch(wait) {
+	case PTP_EVENT_CHECK:
+                result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptp_usb->timeout);
+		if (result==0)
+                        result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptp_usb->timeout);
+		if (result < 0) ret = PTP_ERROR_IO;
+		break;
+	case PTP_EVENT_CHECK_FAST:
+                result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptp_usb->timeout);
+		if (result==0)
+                        result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptp_usb->timeout);
+		if (result < 0) ret = PTP_ERROR_IO;
+		break;
+	default:
+		ret=PTP_ERROR_BADPARAM;
+		break;
+	}
+	if (ret!=PTP_RC_OK) {
+		libusb_glue_error (params,
+			"PTP: reading event an error 0x%04x occurred", ret);
+		return PTP_ERROR_IO;
+	}
+	rlen = result;
+	if (rlen < 8) {
+		libusb_glue_error (params,
+			"PTP: reading event an short read of %ld bytes occurred", rlen);
+		return PTP_ERROR_IO;
+	}
+	/* if we read anything over interrupt endpoint it must be an event */
+	/* build an appropriate PTPContainer */
+	event->Code=dtoh16(usbevent.code);
+	event->SessionID=params->session_id;
+	event->Transaction_ID=dtoh32(usbevent.trans_id);
+	event->Param1=dtoh32(usbevent.param1);
+	event->Param2=dtoh32(usbevent.param2);
+	event->Param3=dtoh32(usbevent.param3);
+	return ret;
+}
+
+uint16_t
+ptp_usb_event_check (PTPParams* params, PTPContainer* event) {
+
+	return ptp_usb_event (params, event, PTP_EVENT_CHECK_FAST);
+}
+
+uint16_t
+ptp_usb_event_wait (PTPParams* params, PTPContainer* event) {
+
+	return ptp_usb_event (params, event, PTP_EVENT_CHECK);
+}
+
+uint16_t
+ptp_usb_control_cancel_request (PTPParams *params, uint32_t transactionid) {
+	PTP_USB *ptp_usb = (PTP_USB *)(params->data);
+	int ret;
+	unsigned char buffer[6];
+
+	htod16a(&buffer[0],PTP_EC_CancelTransaction);
+	htod32a(&buffer[2],transactionid);
+	ret = usb_control_msg(ptp_usb->handle, 
+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                              0x64, 0x0000, 0x0000, (char *) buffer, sizeof(buffer), ptp_usb->timeout);
+	if (ret < sizeof(buffer))
+		return PTP_ERROR_IO;
+	return PTP_RC_OK;
+}
+
+static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev)
+{
+  usb_dev_handle *device_handle;
+  
+  params->sendreq_func=ptp_usb_sendreq;
+  params->senddata_func=ptp_usb_senddata;
+  params->getresp_func=ptp_usb_getresp;
+  params->getdata_func=ptp_usb_getdata;
+  params->cancelreq_func=ptp_usb_control_cancel_request;
+  params->data=ptp_usb;
+  params->transaction_id=0;
+  /*
+   * This is hardcoded here since we have no devices whatsoever that are BE.
+   * Change this the day we run into our first BE device (if ever).
+   */
+  params->byteorder = PTP_DL_LE;
+  
+  ptp_usb->timeout = USB_TIMEOUT_DEFAULT;
+  
+  device_handle = usb_open(dev);
+  if (!device_handle) {
+    perror("usb_open()");
+    return -1;
+  }
+
+  ptp_usb->handle = device_handle;
+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+  /*
+  * If this device is known to be wrongfully claimed by other kernel
+  * drivers (such as mass storage), then try to unload it to make it
+  * accessible from user space.
+  */
+  if (FLAG_UNLOAD_DRIVER(ptp_usb)) {
+    if (usb_detach_kernel_driver_np(device_handle, (int) ptp_usb->interface)) {
+	  // Totally ignore this error!
+	  // perror("usb_detach_kernel_driver_np()");
+    }
+  }
+#endif
+#ifdef __WIN32__
+  // Only needed on Windows, and cause problems on other platforms.
+  if (usb_set_configuration(device_handle, dev->config->bConfigurationValue)) {
+    perror("usb_set_configuration()");
+    return -1;
+  }
+#endif
+  if (usb_claim_interface(device_handle, (int) ptp_usb->interface)) {
+    perror("usb_claim_interface()");
+    return -1;
+  }
+
+  return 0;
+}
+
+static void clear_stall(PTP_USB* ptp_usb)
+{
+  uint16_t status;
+  int ret;
+  
+  /* check the inep status */
+  status = 0;
+  ret = usb_get_endpoint_status(ptp_usb,ptp_usb->inep,&status);
+  if (ret<0) {
+    perror ("inep: usb_get_endpoint_status()");
+  } else if (status) {
+    printf("Clearing stall on IN endpoint\n");
+    ret = usb_clear_stall_feature(ptp_usb,ptp_usb->inep);
+    if (ret<0) {
+      perror ("usb_clear_stall_feature()");
+    }
+  }
+  
+  /* check the outep status */
+  status=0;
+  ret = usb_get_endpoint_status(ptp_usb,ptp_usb->outep,&status);
+  if (ret<0) {
+    perror("outep: usb_get_endpoint_status()");
+  } else if (status) {
+    printf("Clearing stall on OUT endpoint\n");
+    ret = usb_clear_stall_feature(ptp_usb,ptp_usb->outep);
+    if (ret<0) {
+      perror("usb_clear_stall_feature()");
+    }
+  }
+
+  /* TODO: do we need this for INTERRUPT (ptp_usb->intep) too? */
+}
+
+static void clear_halt(PTP_USB* ptp_usb)
+{
+  int ret;
+
+  ret = usb_clear_halt(ptp_usb->handle,ptp_usb->inep);
+  if (ret<0) {
+    perror("usb_clear_halt() on IN endpoint");
+  }
+  ret = usb_clear_halt(ptp_usb->handle,ptp_usb->outep);
+  if (ret<0) {
+    perror("usb_clear_halt() on OUT endpoint");
+  }
+  ret = usb_clear_halt(ptp_usb->handle,ptp_usb->intep);
+  if (ret<0) {
+    perror("usb_clear_halt() on INTERRUPT endpoint");
+  }
+}
+
+static void close_usb(PTP_USB* ptp_usb)
+{
+  // Commented out since it was confusing some
+  // devices to do these things.
+  if (!FLAG_NO_RELEASE_INTERFACE(ptp_usb)) {
+
+    /*
+     * Clear any stalled endpoints
+     * On misbehaving devices designed for Windows/Mac, quote from:
+     * http://www2.one-eyed-alien.net/~mdharm/linux-usb/target_offenses.txt
+     * Device does Bad Things(tm) when it gets a GET_STATUS after CLEAR_HALT
+     * (...) Windows, when clearing a stall, only sends the CLEAR_HALT command, 
+     * and presumes that the stall has cleared.  Some devices actually choke 
+     * if the CLEAR_HALT is followed by a GET_STATUS (used to determine if the 
+     * STALL is persistant or not).
+     */
+    clear_stall(ptp_usb);
+    // Clear halts on any endpoints
+    clear_halt(ptp_usb);
+    // Added to clear some stuff on the OUT endpoint
+    // TODO: is this good on the Mac too?
+    // HINT: some devices may need that you comment these two out too.
+    usb_resetep(ptp_usb->handle, ptp_usb->outep);
+    usb_release_interface(ptp_usb->handle, (int) ptp_usb->interface);
+  }
+
+  usb_close(ptp_usb->handle);
+}
+
+/**
+ * Self-explanatory?
+ */
+static void find_interface_and_endpoints(struct usb_device *dev, 
+					 uint8_t *interface,
+					 int* inep, 
+					 int* inep_maxpacket, 
+					 int* outep, 
+					 int *outep_maxpacket, 
+					 int* intep)
+{
+  int i;
+
+  // Loop over the device configurations
+  for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+    uint8_t j;
+
+    for (j = 0; j < dev->config[i].bNumInterfaces; j++) {
+      uint8_t k;
+      uint8_t no_ep;
+      struct usb_endpoint_descriptor *ep;
+      
+      if (dev->descriptor.bNumConfigurations > 1 || dev->config[i].bNumInterfaces > 1) {
+	// OK This device has more than one interface, so we have to find out
+	// which one to use! 
+	// FIXME: Probe the interface.
+	// FIXME: Release modules attached to all other interfaces in Linux...?
+      }
+
+      *interface = dev->config[i].interface[j].altsetting->bInterfaceNumber;
+      ep = dev->config[i].interface[j].altsetting->endpoint;
+      no_ep = dev->config[i].interface[j].altsetting->bNumEndpoints;
+      
+      for (k = 0; k < no_ep; k++) {
+	if (ep[k].bmAttributes==USB_ENDPOINT_TYPE_BULK)	{
+	  if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
+	      USB_ENDPOINT_DIR_MASK)
+	    {
+	      *inep=ep[k].bEndpointAddress;
+	      *inep_maxpacket=ep[k].wMaxPacketSize;
+	    }
+	  if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==0)
+	    {
+	      *outep=ep[k].bEndpointAddress;
+	      *outep_maxpacket=ep[k].wMaxPacketSize;
+	    }
+	} else if (ep[k].bmAttributes==USB_ENDPOINT_TYPE_INTERRUPT){
+	  if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
+	      USB_ENDPOINT_DIR_MASK)
+	    {
+	      *intep=ep[k].bEndpointAddress;
+	    }
+	}
+      }
+      // We assigned the endpoints so return here.
+      return;
+    }
+  }
+}
+
+/**
+ * This function assigns params and usbinfo given a raw device
+ * as input.
+ * @param device the device to be assigned.
+ * @param usbinfo a pointer to the new usbinfo.
+ * @return an error code.
+ */
+LIBMTP_error_number_t configure_usb_device(LIBMTP_raw_device_t *device, 
+					   PTPParams *params,
+					   void **usbinfo)
+{
+  PTP_USB *ptp_usb;
+  struct usb_device *libusb_device;
+  uint16_t ret = 0;
+  struct usb_bus *bus;
+  int found = 0;
+
+  /* See if we can find this raw device again... */
+  bus = init_usb();
+  for (; bus != NULL; bus = bus->next) {
+    if (bus->location == device->bus_location) {
+      struct usb_device *dev = bus->devices;
+
+      for (; dev != NULL; dev = dev->next) {
+	if(dev->devnum == device->devnum &&
+	   dev->descriptor.idVendor == device->device_entry.vendor_id &&
+	   dev->descriptor.idProduct == device->device_entry.product_id ) {
+	  libusb_device = dev;
+	  found = 1;
+	  break;
+	}
+      }
+      if (found)
+	break;
+    }
+  }
+  /* Device has gone since detecting raw devices! */
+  if (!found) {
+    return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
+  }
+
+  /* Allocate structs */
+  ptp_usb = (PTP_USB *) malloc(sizeof(PTP_USB));
+  if (ptp_usb == NULL) {
+    return LIBMTP_ERROR_MEMORY_ALLOCATION;
+  }
+  /* Start with a blank slate (includes setting device_flags to 0) */
+  memset(ptp_usb, 0, sizeof(PTP_USB));
+
+  /* Copy the raw device */
+  memcpy(&ptp_usb->rawdevice, device, sizeof(LIBMTP_raw_device_t));
+
+  /*
+   * Some devices must have their "OS Descriptor" massaged in order
+   * to work.
+   */
+  if (FLAG_ALWAYS_PROBE_DESCRIPTOR(ptp_usb)) {
+    // Massage the device descriptor
+    (void) probe_device_descriptor(libusb_device, NULL);
+  }
+  
+
+  /* Assign endpoints to usbinfo... */
+  find_interface_and_endpoints(libusb_device,
+		   &ptp_usb->interface,
+		   &ptp_usb->inep,
+		   &ptp_usb->inep_maxpacket,
+		   &ptp_usb->outep,
+		   &ptp_usb->outep_maxpacket,
+		   &ptp_usb->intep);
+    
+  /* Attempt to initialize this device */
+  if (init_ptp_usb(params, ptp_usb, libusb_device) < 0) {
+    fprintf(stderr, "LIBMTP PANIC: Unable to initialize device\n");
+    return LIBMTP_ERROR_CONNECTING;
+  }
+  
+  /*
+   * This works in situations where previous bad applications
+   * have not used LIBMTP_Release_Device on exit 
+   */
+  if ((ret = ptp_opensession(params, 1)) == PTP_ERROR_IO) {
+    fprintf(stderr, "PTP_ERROR_IO: Trying again after re-initializing USB interface\n");
+    close_usb(ptp_usb);
+      
+    if(init_ptp_usb(params, ptp_usb, libusb_device) <0) {
+      fprintf(stderr, "LIBMTP PANIC: Could not open session on device\n");
+      return LIBMTP_ERROR_CONNECTING;
+    }
+    
+    /* Device has been reset, try again */
+    ret = ptp_opensession(params, 1);
+  }
+  
+  /* Was the transaction id invalid? Try again */
+  if (ret == PTP_RC_InvalidTransactionID) {
+    fprintf(stderr, "LIBMTP WARNING: Transaction ID was invalid, increment and try again\n");
+    params->transaction_id += 10;
+    ret = ptp_opensession(params, 1);
+  }
+
+  if (ret != PTP_RC_SessionAlreadyOpened && ret != PTP_RC_OK) {
+    fprintf(stderr, "LIBMTP PANIC: Could not open session! "
+	    "(Return code %d)\n  Try to reset the device.\n",
+	    ret);
+    usb_release_interface(ptp_usb->handle,
+			  (int) ptp_usb->interface);
+    return LIBMTP_ERROR_CONNECTING;
+  }
+
+  /* OK configured properly */
+  *usbinfo = (void *) ptp_usb;
+  return LIBMTP_ERROR_NONE;
+}
+
+
+void close_device (PTP_USB *ptp_usb, PTPParams *params)
+{
+  if (ptp_closesession(params)!=PTP_RC_OK)
+    fprintf(stderr,"ERROR: Could not close session!\n");
+  close_usb(ptp_usb);
+}
+
+void set_usb_device_timeout(PTP_USB *ptp_usb, int timeout)
+{
+    ptp_usb->timeout = timeout;
+}
+
+void get_usb_device_timeout(PTP_USB *ptp_usb, int *timeout)
+{
+    *timeout = ptp_usb->timeout;
+}
+
+static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep)
+{
+  
+  return (usb_control_msg(ptp_usb->handle,
+			  USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE, USB_FEATURE_HALT,
+                          ep, NULL, 0, ptp_usb->timeout));
+}
+
+static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status)
+{
+  return (usb_control_msg(ptp_usb->handle,
+			  USB_DP_DTH|USB_RECIP_ENDPOINT, USB_REQ_GET_STATUS,
+                          USB_FEATURE_HALT, ep, (char *)status, 2, ptp_usb->timeout));
+}
diff --git a/src/libusb-glue.h b/src/libusb-glue.h
new file mode 100644
index 0000000..c68677e
--- /dev/null
+++ b/src/libusb-glue.h
@@ -0,0 +1,129 @@
+/**
+ * \file libusb-glue.h
+ * Low-level USB interface glue towards libusb.
+ *
+ * Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2006-2007 Marcus Meissner
+ * Copyright (C) 2007 Ted Bullock
+ * Copyright (C) 2008 Chris Bagwell <chris@cnpbagwell.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Created by Richard Low on 24/12/2005.
+ * Modified by Linus Walleij
+ *
+ */
+#ifndef LIBUSB_GLUE_H
+#define LIBUSB_GLUE_H
+
+#include "ptp.h"
+#include <usb.h>
+#include "libmtp.h"
+#include "device-flags.h"
+
+/* Make functions available for C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define USB_BULK_READ usb_bulk_read
+#define USB_BULK_WRITE usb_bulk_write
+
+/**
+ * Internal USB struct.
+ */
+typedef struct _PTP_USB PTP_USB;
+struct _PTP_USB {
+  PTPParams *params;
+  usb_dev_handle* handle;
+  uint8_t interface;
+  int inep;
+  int inep_maxpacket;
+  int outep;
+  int outep_maxpacket;
+  int intep;
+  int timeout;
+  /** File transfer callbacks and counters */
+  int callback_active;
+  uint64_t current_transfer_total;
+  uint64_t current_transfer_complete;
+  LIBMTP_progressfunc_t current_transfer_callback;
+  void const * current_transfer_callback_data;
+  /** Any special device flags, only used internally */
+  LIBMTP_raw_device_t rawdevice;
+};
+
+int open_device (int busn, int devn, short force, PTP_USB *ptp_usb, PTPParams *params, struct usb_device **dev);
+void dump_usbinfo(PTP_USB *ptp_usb);
+const char *get_playlist_extension(PTP_USB *ptp_usb);
+void close_device(PTP_USB *ptp_usb, PTPParams *params);
+LIBMTP_error_number_t configure_usb_device(LIBMTP_raw_device_t *device, 
+					   PTPParams *params,
+					   void **usbinfo);
+void set_usb_device_timeout(PTP_USB *ptp_usb, int timeout);
+void get_usb_device_timeout(PTP_USB *ptp_usb, int *timeout);
+
+/* Flag check macros */
+#define FLAG_BROKEN_MTPGETOBJPROPLIST_ALL(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL)
+#define FLAG_UNLOAD_DRIVER(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_UNLOAD_DRIVER)
+#define FLAG_BROKEN_MTPGETOBJPROPLIST(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)
+#define FLAG_NO_ZERO_READS(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_NO_ZERO_READS)
+#define FLAG_IRIVER_OGG_ALZHEIMER(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_IRIVER_OGG_ALZHEIMER)
+#define FLAG_ONLY_7BIT_FILENAMES(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES)
+#define FLAG_NO_RELEASE_INTERFACE(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_NO_RELEASE_INTERFACE)
+#define FLAG_IGNORE_HEADER_ERRORS(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_IGNORE_HEADER_ERRORS)
+#define FLAG_BROKEN_SET_OBJECT_PROPLIST(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST)
+#define FLAG_OGG_IS_UNKNOWN(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_OGG_IS_UNKNOWN)
+#define FLAG_BROKEN_SET_SAMPLE_DIMENSIONS(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_SET_SAMPLE_DIMENSIONS)
+#define FLAG_ALWAYS_PROBE_DESCRIPTOR(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR)
+#define FLAG_PLAYLIST_SPL_V1(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_PLAYLIST_SPL_V1)
+#define FLAG_PLAYLIST_SPL_V2(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_PLAYLIST_SPL_V2)
+#define FLAG_PLAYLIST_SPL(a) \
+  ((a)->rawdevice.device_entry.device_flags & (DEVICE_FLAG_PLAYLIST_SPL_V1 | DEVICE_FLAG_PLAYLIST_SPL_V2))
+#define FLAG_CANNOT_HANDLE_DATEMODIFIED(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED)
+#define FLAG_BROKEN_SEND_OBJECT_PROPLIST(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST)
+#define FLAG_BROKEN_BATTERY_LEVEL(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_BROKEN_BATTERY_LEVEL)
+#define FLAG_FLAC_IS_UNKNOWN(a) \
+  ((a)->rawdevice.device_entry.device_flags & DEVICE_FLAG_FLAC_IS_UNKNOWN)
+
+/* connect_first_device return codes */
+#define PTP_CD_RC_CONNECTED	0
+#define PTP_CD_RC_NO_DEVICES	1
+#define PTP_CD_RC_ERROR_CONNECTING	2
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif //  LIBUSB-GLUE_H
diff --git a/src/music-players.h b/src/music-players.h
new file mode 100644
index 0000000..4e1caab
--- /dev/null
+++ b/src/music-players.h
@@ -0,0 +1,969 @@
+/**
+ * \file music-players.h
+ * List of music players as USB ids.
+ *
+ * Copyright (C) 2005-2007 Richard A. Low <richard@wentnet.com>
+ * Copyright (C) 2005-2009 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2006-2007 Marcus Meissner
+ * Copyright (C) 2007 Ted Bullock
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * This file is supposed to be included within a struct from both libmtp
+ * and libgphoto2.
+ *
+ * Information can be harvested from Windows driver .INF files, see:
+ * http://msdn.microsoft.com/en-us/library/aa973606.aspx
+ */
+/*
+ * MTP device list, trying real bad to get all devices into
+ * this list by stealing from everyone I know.
+ * Some devices taken from the Rockbox device listing:
+ * http://www.rockbox.org/twiki/bin/view/Main/DeviceDetection
+ */
+
+  /*
+   * Creative Technology and ZiiLABS
+   * Initially the Creative devices was all we supported so these are
+   * the most thoroughly tested devices. Presumably only the devices
+   * with older firmware (the ones that have 32bit object size) will
+   * need the DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL flag. This bug
+   * manifest itself when you have a lot of folders on the device,
+   * some of the folders will start to disappear when getting all objects
+   * and properties.
+   */
+  { "Motorola", 0x22b8, "Xoom", 0x70a9, DEVICE_FLAG_NONE },
+  { "Motorola", 0x22b8, "Xoom", 0x70a8 , DEVICE_FLAG_NONE},
+  { "Creative", 0x041e, "ZEN Vision", 0x411f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "Portable Media Center", 0x4123, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Xtra (MTP mode)", 0x4128, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Dell", 0x041e, "DJ (2nd generation)", 0x412f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Micro (MTP mode)", 0x4130, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Touch (MTP mode)", 0x4131, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Dell", 0x041e, "Dell Pocket DJ (MTP mode)", 0x4132, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Sleek (MTP mode)", 0x4137, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN MicroPhoto", 0x413c, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Sleek Photo", 0x413d, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Vision:M", 0x413e, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by marazm@o2.pl
+  { "Creative", 0x041e, "ZEN V", 0x4150, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by danielw@iinet.net.au
+  // This version of the Vision:M needs the no release interface flag,
+  // unclear whether the other version above need it too or not.
+  { "Creative", 0x041e, "ZEN Vision:M (DVP-HD0004)", 0x4151, 
+      DEVICE_FLAG_NO_RELEASE_INTERFACE |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL},
+  // Reported by Darel on the XNJB forums
+  { "Creative", 0x041e, "ZEN V Plus", 0x4152,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  { "Creative", 0x041e, "ZEN Vision W", 0x4153,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Don't add 0x4155: this is a Zen Stone device which is not MTP
+  // Reported by Paul Kurczaba <paul@kurczaba.com>
+  { "Creative", 0x041e, "ZEN", 0x4157, DEVICE_FLAG_IGNORE_HEADER_ERRORS |
+    DEVICE_FLAG_BROKEN_SET_SAMPLE_DIMENSIONS |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by Ringofan <mcroman@users.sourceforge.net>
+  { "Creative", 0x041e, "ZEN V 2GB", 0x4158,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by j norment <stormzen@gmail.com>
+  { "Creative", 0x041e, "ZEN Mozaic", 0x4161,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by Aaron F. Gonzalez <sub_tex@users.sourceforge.net>
+  { "Creative", 0x041e, "ZEN X-Fi", 0x4162,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by Todor Gyumyushev <yodor1@users.sourceforge.net>
+  { "ZiiLABS", 0x041e, "Zii EGG", 0x6000,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+
+  /*
+   * Samsung
+   * We suspect that more of these are dual mode.
+   * We suspect more of these might need DEVICE_FLAG_NO_ZERO_READS
+   * We suspect more of these might need DEVICE_FLAG_PLAYLIST_SPL_V1
+   *  or DEVICE_FLAG_PLAYLIST_SPL_V2 to get playlists working.
+   * YP-NEU, YP-NDU, YP-20, YP-800, YP-MF Series, YP-100, YP-30
+   * YP-700 and YP-90 are NOT MTP, but use a Samsung custom protocol.
+   */
+  // From anonymous SourceForge user, not verified
+  { "Samsung", 0x04e8, "YP-900", 0x0409, DEVICE_FLAG_NONE },
+  // From MItch <dbaker@users.sourceforge.net>
+  { "Samsung", 0x04e8, "I550W Phone", 0x04a4, DEVICE_FLAG_NONE },
+  // From Gabriel Nunes <gabrielkm1@yahoo.com.br>
+  { "Samsung", 0x04e8, "YH-920 (501d)", 0x501d, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Soren O'Neill
+  { "Samsung", 0x04e8, "YH-920 (5022)", 0x5022, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Contributed by aronvanammers on SourceForge
+  { "Samsung", 0x04e8, "YH-925GS", 0x5024, DEVICE_FLAG_NONE },
+  // From libgphoto2, according to tests by Stephan Fabel it cannot
+  // get all objects with the getobjectproplist command..
+  { "Samsung", 0x04e8, "YH-820", 0x502e, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Contributed by polux2001@users.sourceforge.net
+  { "Samsung", 0x04e8, "YH-925(-GS)", 0x502f, DEVICE_FLAG_UNLOAD_DRIVER | 
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Contributed by anonymous person on SourceForge
+  { "Samsung", 0x04e8, "YH-J70J", 0x5033, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From XNJB user
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-Z5", 0x503c, DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // Don't add 0x5041 as this is YP-Z5 in USB mode
+  // Contributed by anonymous person on SourceForge
+  { "Samsung", 0x04e8, "YP-T7J", 0x5047, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by cstrickler@gmail.com
+  { "Samsung", 0x04e8, "YP-U2J (YP-U2JXB/XAA)", 0x5054, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Andrew Benson
+  { "Samsung", 0x04e8, "YP-F2J", 0x5057, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Patrick <skibler@gmail.com>
+  // Just guessing but looks like .spl v1 http://www.anythingbutipod.com/forum/showthread.php?t=14160
+  { "Samsung", 0x04e8, "YP-K5", 0x505a, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From dev.local@gmail.com - 0x4e8/0x507c is the UMS mode, apparently
+  // do not add that device.
+  // From m.eik michalke
+  // This device does NOT use the special SPL playlist according to sypqgjxu@gmx.de.
+  { "Samsung", 0x04e8, "YP-U3", 0x507d, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Matthew Wilcox <matthew@wil.cx>
+  // Sergio <sfrdll@tiscali.it> reports this device need the BROKEN ALL flag.
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-T9", 0x507f, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Paul Clinch
+  // Just guessing but looks like .spl v1 http://www.anythingbutipod.com/forum/showthread.php?t=14160
+  // Some versions of the firmware reportedly support OGG, reportedly only the
+  // UMS versions, so MTP+OGG is not possible on this device.
+  { "Samsung", 0x04e8, "YP-K3", 0x5081, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From XNJB user
+  // From Alistair Boyle, .spl v2 required for playlists
+  // According to the device log it properly supports OGG
+  { "Samsung", 0x04e8, "YP-P2", 0x5083, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_PLAYLIST_SPL_V2 },
+  // From Paul Clinch
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-T10", 0x508a, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 | DEVICE_FLAG_NO_ZERO_READS },
+  // From Wim Verwimp <wimverwimp@gmail.com>
+  // Not sure about the Ogg and broken proplist flags here. Just guessing.
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-S5", 0x508b, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Ludovic Danigo
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-S3", 0x5091, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Adrian Levi <adrian.levi@gmail.com>
+  // Guessing on .spl flag
+  // This one supports OGG properly through the correct MTP type.
+  { "Samsung", 0x04e8, "YP-U4", 0x5093, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Anonymous SourceForge user, NOT VERIFIED TO BE MTP
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-Q1", 0x5115, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Anonymous SourceForge user
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-P3", 0x511a, DEVICE_FLAG_UNLOAD_DRIVER |
+     DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+     DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Anonymous SourceForge user
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-Q2", 0x511d, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+      DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From Marco Pizzocaro <mpizzocaro@users.sourceforge.net>
+  // Guessing on .spl flag
+  { "Samsung", 0x04e8, "YP-U5", 0x5121,
+     DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From a rouge .INF file,
+  // this device ID seems to have been recycled for:
+  // the Samsung SGH-A707 Cingular cellphone
+  // the Samsung L760-V cellphone
+  // the Samsung SGH-U900 cellphone
+  { "Samsung", 0x04e8, "YH-999 Portable Media Center/SGH-A707/SGH-L760V/SGH-U900", 0x5a0f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // From Santi Béjar <sbejar@gmail.com> - not sure this is MTP...
+  // { "Samsung", 0x04e8, "Z170 Mobile Phone", 0x6601, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Santi Béjar <sbejar@gmail.com> - not sure this is MTP...
+  // { "Samsung", 0x04e8, "E250 Mobile Phone", 0x663e, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Lionel Bouton
+  { "Samsung", 0x04e8, "X830 Mobile Phone", 0x6702, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From James <jamestech@gmail.com>
+  { "Samsung", 0x04e8, "U600 Mobile Phone", 0x6709, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Cesar Cardoso <cesar@cesarcardoso.tk>
+  // No confirmation that this is really MTP.
+  { "Samsung", 0x04e8, "F250 Mobile Phone", 0x6727, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Charlie Todd  2007-10-31
+  { "Samsung", 0x04e8, "Juke (SCH-U470)", 0x6734, DEVICE_FLAG_UNLOAD_DRIVER},
+  // Reported by Tenn
+  { "Samsung", 0x04e8, "GT-B2700", 0x6752, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Added by Greg Fitzgerald <netzdamon@gmail.com>
+  { "Samsung", 0x04e8, "SAMSUNG Trance", 0x6763, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_PLAYLIST_SPL_V1 },
+  // From anonymous Sourceforge user
+  { "Samsung", 0x04e8, "GT-S5230", 0xe20c, DEVICE_FLAG_NONE },
+
+
+
+  /*
+   * Intel
+   */
+  { "Intel", 0x045e, "Bandon Portable Media Center", 0x00c9, DEVICE_FLAG_NONE },
+  // Reported by Tadimarri Sarath <sarath.tadi@gmail.com>
+  // No idea why this use an Intel PID, perhaps a leftover from
+  // the early PMC development days when Intel and Microsoft were
+  // partnering.
+  { "Microsoft", 0x045e, "Windows MTP Simulator", 0x0622, DEVICE_FLAG_NONE },
+
+  /*
+   * JVC
+   */
+  // From Mark Veinot
+  { "JVC", 0x04f1, "Alneo XA-HD500", 0x6105, DEVICE_FLAG_NONE },
+
+  /*
+   * Philips
+   */
+  { "Philips", 0x0471, "HDD6320/00 or HDD6330/17", 0x014b, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Anonymous SourceForge user
+  { "Philips", 0x0471, "HDD14XX,HDD1620 or HDD1630/17", 0x014c, DEVICE_FLAG_NONE },
+  // from discussion forum
+  { "Philips", 0x0471, "HDD085/00 or HDD082/17", 0x014d, DEVICE_FLAG_NONE },
+  // from XNJB forum
+  { "Philips", 0x0471, "GoGear SA9200", 0x014f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // From John Coppens <jcoppens@users.sourceforge.net>
+  { "Philips", 0x0471, "SA1115/55", 0x0164, DEVICE_FLAG_NONE },
+  // From Gerhard Mekenkamp
+  { "Philips", 0x0471, "GoGear Audio", 0x0165, DEVICE_FLAG_NONE },
+  // from David Holm <wormie@alberg.dk>
+  { "Philips", 0x0471, "Shoqbox", 0x0172, DEVICE_FLAG_ONLY_7BIT_FILENAMES },
+  // from npedrosa
+  { "Philips", 0x0471, "PSA610", 0x0181, DEVICE_FLAG_NONE },
+  // From libgphoto2 source
+  { "Philips", 0x0471, "HDD6320", 0x01eb, DEVICE_FLAG_NONE },
+  // From Detlef Meier <dm@emlix.com>
+  { "Philips", 0x0471, "GoGear SA6014/SA6015/SA6024/SA6025/SA6044/SA6045", 0x084e, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From anonymous Sourceforge user SA5145/02
+  { "Philips", 0x0471, "GoGear SA5145", 0x0857, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From a
+  { "Philips", 0x0471, "GoGear SA6125/SA6145/SA6185", 0x2002, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From anonymous Sourceforge user, not verified to be MTP!
+  { "Philips", 0x0471, "GoGear SA3345", 0x2004, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Roberto Vidmar <rvidmar@libero.it>
+  { "Philips", 0x0471, "SA5285", 0x2022, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Elie De Brauwer <elie@de-brauwer.be>
+  { "Philips", 0x0471, "GoGear ViBE SA1VBE04", 0x2075,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Elie De Brauwer <elie@de-brauwer.be>
+  { "Philips", 0x0471, "GoGear ViBE SA1VBE08", 0x207b,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+  // From josmtx <josmtx@users.sourceforge.net>
+  { "Philips", 0x0471, "GoGear Aria", 0x207c,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+  // from XNJB user
+  { "Philips", 0x0471, "PSA235", 0x7e01, DEVICE_FLAG_NONE },
+
+
+  /*
+   * SanDisk
+   * several devices (c150 for sure) are definately dual-mode and must 
+   * have the USB mass storage driver that hooks them unloaded first.
+   * They all have problematic dual-mode making the device unload effect
+   * uncertain on these devices.
+   *
+   * All older devices seem to need DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL.
+   * Old chipsets: e200/c200 use PP5024 from Nvidia (formerly PortalPlayer).
+   * m200 use TCC770 from Telechips.
+   *
+   * The newer Sansa v2 chipset, AD3525 from Austriamicrosystems (AMS) found 
+   * in e280 v2 c200 v2, Clip, Fuze etc require DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST
+   * and DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR to work properly.
+   *
+   * For more info see: http://daniel.haxx.se/sansa/v2.html
+   */
+  // Reported by Brian Robison
+  { "SanDisk", 0x0781, "Sansa m230/m240", 0x7400,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // From Rockbox device listing
+  { "SanDisk", 0x0781, "Sansa m200-tcc (MTP mode)", 0x7401,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by tangent_@users.sourceforge.net
+  { "SanDisk", 0x0781, "Sansa c150", 0x7410,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // From libgphoto2 source
+  // Reported by <gonkflea@users.sourceforge.net>
+  // Reported by Mike Owen <mikeowen@computerbaseusa.com>
+  { "SanDisk", 0x0781, "Sansa e200/e250/e260/e270/e280", 0x7420,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Don't add 0x7421 as this is e280 in MSC mode
+  // Reported by XNJB user
+  { "SanDisk", 0x0781, "Sansa e260/e280 v2", 0x7422,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR |
+    DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by XNJB user
+  { "SanDisk", 0x0781, "Sansa m240/m250", 0x7430,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by Eugene Brevdo <ebrevdo@princeton.edu>
+  { "SanDisk", 0x0781, "Sansa Clip", 0x7432,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR |
+    DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED},
+  // Reported by HackAR <hackar@users.sourceforge.net>
+  { "SanDisk", 0x0781, "Sansa Clip v2", 0x7434,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR |
+    DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED},
+  // Reported by anonymous user at sourceforge.net
+  { "SanDisk", 0x0781, "Sansa c240/c250", 0x7450,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by anonymous SourceForge user
+  { "SanDisk", 0x0781, "Sansa c250 v2", 0x7452,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by Troy Curtis Jr.
+  { "SanDisk", 0x0781, "Sansa Express", 0x7460,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by XNJB user, and Miguel de Icaza <miguel@gnome.org>
+  // This has no dual-mode so no need to unload any driver.
+  // This is a Linux based device!
+  { "SanDisk", 0x0781, "Sansa Connect", 0x7480, DEVICE_FLAG_NONE },
+  // Reported by anonymous SourceForge user
+  { "SanDisk", 0x0781, "Sansa View", 0x74b0,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Reported by Patrick <skibler@gmail.com>
+  // There are apparently problems with this device.
+  { "SanDisk", 0x0781, "Sansa Fuze", 0x74c0,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR |
+    DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+  // Harry Phillips <tuxcomputers@users.sourceforge.net>
+  { "SanDisk", 0x0781, "Sansa Fuze v2", 0x74c2,
+    DEVICE_FLAG_UNLOAD_DRIVER |  DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR |
+    DEVICE_FLAG_CANNOT_HANDLE_DATEMODIFIED },
+
+  /*
+   * iRiver
+   * we assume that PTP_OC_MTP_GetObjPropList is essentially
+   * broken on all iRiver devices, meaning it simply won't return
+   * all properties for a file when asking for metadata 0xffffffff. 
+   * Please test on your device if you believe it isn't broken!
+   */
+  { "iRiver", 0x1006, "H300 Series MTP", 0x3004,
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x1006, "Portable Media Center", 0x4002,
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x1006, "Portable Media Center", 0x4003,
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // From [st]anislav <iamstanislav@gmail.com>
+  { "iRiver", 0x1042, "T7 Volcano", 0x1143, DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // From an anonymous person at SourceForge, uncertain about this one
+  { "iRiver", 0x4102, "iFP-880", 0x1008, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // 0x4102, 0x1042 is a USB mass storage mode for E100 v2/Lplayer
+  // From libgphoto2 source
+  { "iRiver", 0x4102, "T10", 0x1113, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x4102, "T20 FM", 0x1114, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // This appears at the MTP-UMS site
+  { "iRiver", 0x4102, "T20", 0x1115, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x4102, "U10", 0x1116, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x4102, "T10a", 0x1117, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x4102, "T20", 0x1118, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  { "iRiver", 0x4102, "T30", 0x1119, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Reported by David Wolpoff
+  { "iRiver", 0x4102, "T10 2GB", 0x1120, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Rough guess this is the MTP device ID...
+  { "iRiver", 0x4102, "N12", 0x1122, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Reported by Philip Antoniades <philip@mysql.com>
+  // Newer iriver devices seem to have shaped-up firmware without any
+  // of the annoying bugs.
+  { "iRiver", 0x4102, "Clix2", 0x1126, DEVICE_FLAG_NONE },
+  // Reported by Adam Torgerson
+  { "iRiver", 0x4102, "Clix", 0x112a, 
+    DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Reported by Douglas Roth <dougaus@gmail.com>
+  { "iRiver", 0x4102, "X20", 0x1132, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Reported by Robert Ugo <robert_ugo@users.sourceforge.net>
+  { "iRiver", 0x4102, "T60", 0x1134, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_IRIVER_OGG_ALZHEIMER },
+  // Reported by two anonymous SourceForge users
+  // Needs the stronger OGG_IS_UNKNOWN flag to support OGG properly,
+  // be aware of newer players that may be needing this too.
+  { "iRiver", 0x4102, "E100", 0x1141, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+  // Reported by anonymous SourceForge user
+  // Need verification of whether this firmware really need all these flags
+  { "iRiver", 0x4102, "E100 v2/Lplayer", 0x1142, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+  // Reported by Richard Vennemann <vennemann@users.sourceforge.net>
+  // In USB Mass Storage mode it is 0x4102/0x1047
+  // Seems to use the new shaped-up firmware.
+  { "iRiver", 0x4102, "Spinn", 0x1147, DEVICE_FLAG_NONE },
+  // Reported by Tony Janssen <tonyjanssen@users.sourceforge.net>
+  { "iRiver", 0x4102, "E50", 0x1151,
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+  // Reported by Scott Call
+  // Assume this actually supports OGG though it reports it doesn't.
+  { "iRiver", 0x4102, "H10 20GB", 0x2101, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+  { "iRiver", 0x4102, "H10 5GB", 0x2102, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+  // From Rockbox device listing
+  { "iRiver", 0x4102, "H10 5.6GB", 0x2105, 
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS | 
+    DEVICE_FLAG_OGG_IS_UNKNOWN },
+
+
+  /*
+   * Dell
+   */
+  { "Dell, Inc", 0x413c, "DJ Itty", 0x4500, DEVICE_FLAG_NONE },
+  
+  /*
+   * Toshiba
+   * Tentatively flagged all Toshiba devices with
+   * DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST after one of them
+   * showed erroneous behaviour.
+   */
+  { "Toshiba", 0x0930, "Gigabeat MEGF-40", 0x0009,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  { "Toshiba", 0x0930, "Gigabeat", 0x000c,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Nicholas Tripp
+  { "Toshiba", 0x0930, "Gigabeat P20", 0x000f,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // From libgphoto2
+  { "Toshiba", 0x0930, "Gigabeat S", 0x0010, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
+      DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Rob Brown
+  { "Toshiba", 0x0930, "Gigabeat P10", 0x0011,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by solanum@users.sourceforge.net
+  { "Toshiba", 0x0930, "Gigabeat V30", 0x0014,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Michael Davis <slithy@yahoo.com>
+  { "Toshiba", 0x0930, "Gigabeat U", 0x0016,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Devon Jacobs <devo@godevo.com>
+  { "Toshiba", 0x0930, "Gigabeat MEU202", 0x0018,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Rolf <japan (at) dl3lar.de>
+  { "Toshiba", 0x0930, "Gigabeat T", 0x0019,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by Phil Ingram <ukpbert@users.sourceforge.net>
+  // Tentatively added - no real reports of this device ID being MTP,
+  // reports as USB Mass Storage currently.
+  { "Toshiba", 0x0930, "Gigabeat MEU201", 0x001a,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+  // Reported by anonymous SourceForge user
+  { "Toshiba", 0x0930, "Gigabeat MET401", 0x001d,
+    DEVICE_FLAG_NO_RELEASE_INTERFACE |  DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+
+  
+  /*
+   * Archos
+   * These devices have some dual-mode interfaces which will really
+   * respect the driver unloading, so DEVICE_FLAG_UNLOAD_DRIVER
+   * really work on these devices!
+   */
+  // Reported by Alexander Haertig <AlexanderHaertig@gmx.de>
+  { "Archos", 0x0e79, "Gmini XS100", 0x1207, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Added by Jan Binder
+  { "Archos", 0x0e79, "XS202 (MTP mode)", 0x1208, DEVICE_FLAG_NONE },
+  // Reported by gudul1@users.sourceforge.net
+  { "Archos", 0x0e79, "104 (MTP mode)", 0x120a, DEVICE_FLAG_NONE },
+  // Reported by anonymous Sourceforge user.
+  { "Archos", 0x0e79, "404 (MTP mode)", 0x1301, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Etienne Chauchot <chauchot.etienne@free.fr>
+  { "Archos", 0x0e79, "504 (MTP mode)", 0x1307, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Kay McCormick <kaym@modsystems.com>
+  { "Archos", 0x0e79, "704 mobile dvr", 0x130d, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Joe Rabinoff
+  { "Archos", 0x0e79, "605 (MTP mode)", 0x1313, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Jim Krehl <jimmuhk@users.sourceforge.net>
+  { "Archos", 0x0e79, "5 (MTP mode)", 0x1331, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Adrien Guichard <tmor@users.sourceforge.net>
+  { "Archos", 0x0e79, "5 (MTP mode)", 0x1333, DEVICE_FLAG_UNLOAD_DRIVER },
+
+  /*
+   * Dunlop (OEM of EGOMAN ltd?) reported by Nanomad
+   * This unit is falsely detected as USB mass storage in Linux
+   * prior to kernel 2.6.19 (fixed by patch from Alan Stern)
+   * so on older kernels special care is needed to remove the
+   * USB mass storage driver that erroneously binds to the device
+   * interface.
+   */
+  { "Dunlop", 0x10d6, "MP3 player 1GB / EGOMAN MD223AFD", 0x2200, DEVICE_FLAG_UNLOAD_DRIVER},
+  // Reported by Steven Black <stevenblack1956@users.sourceforge.net>
+  // Obviously this company goes by many names.
+  // This device is USB 2.0 only. Broken pipe on closing.
+  { "Memorex", 0x10d6, "MMP 8585/8586", 0x2300, DEVICE_FLAG_UNLOAD_DRIVER | 
+      DEVICE_FLAG_NO_RELEASE_INTERFACE},
+  
+  /*
+   * Microsoft
+   */
+  // Reported by Farooq Zaman (used for all Zunes)
+  { "Microsoft", 0x045e, "Zune", 0x0710, DEVICE_FLAG_NONE }, 
+  
+  /*
+   * Sirius
+   */
+  { "Sirius", 0x18f6, "Stiletto", 0x0102, DEVICE_FLAG_NONE },
+  // Reported by Chris Bagwell <chris@cnpbagwell.com>
+  { "Sirius", 0x18f6, "Stiletto 2", 0x0110, DEVICE_FLAG_NONE },
+
+  /*
+   * Canon
+   * This is actually a camera, but it has a Microsoft device descriptor
+   * and reports itself as supporting the MTP extension.
+   */
+  { "Canon", 0x04a9, "PowerShot A640 (PTP/MTP mode)", 0x3139,
+     DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+
+  /*
+   * Nokia
+   * Please verify the low device IDs here, I suspect these might be for
+   * things like USB storage or modem mode actually, whereas the higher
+   * range (0x04nn) could be for MTP. Some of the devices were gathered
+   * from the Nokia WMP drivers:
+   * http://nds2.nokia.com/files/support/global/phones/software/
+   * Address was gathered from going to:
+   * nseries.com
+   * -> support
+   * -> select supported device
+   *  -> PC software
+   *    -> Music software
+   *      -> Windows Media Player 10 driver
+   */
+  // From: DoomHammer <gaczek@users.sourceforge.net>
+  { "Nokia", 0x0421, "N81 Mobile Phone", 0x000a, DEVICE_FLAG_NONE },
+  // From an anonymous SourceForge user
+  { "Nokia", 0x0421, "6120c Classic Mobile Phone", 0x002e, DEVICE_FLAG_NONE },
+  // From Stefano
+  { "Nokia", 0x0421, "N96 Mobile Phone", 0x0039, DEVICE_FLAG_NONE },
+  // From Martijn van de Streek <martijn@vandestreek.net>
+  { "Nokia", 0x0421, "6500c Classic Mobile Phone", 0x003c, DEVICE_FLAG_NONE },
+  // From: DoomHammer <gaczek@users.sourceforge.net>
+  { "Nokia", 0x0421, "3110c Mobile Phone", 0x005f, DEVICE_FLAG_NONE },
+  // From: Vasily <spc-@users.sourceforge.net>
+  { "Nokia", 0x0421, "3109c Mobile Phone", 0x0065, DEVICE_FLAG_NONE },
+  // From: <rawc@users.sourceforge.net>
+  { "Nokia", 0x0421, "5310 XpressMusic", 0x006c, DEVICE_FLAG_NONE },
+  // From: robin (AT) headbank D0Tco DOTuk
+  { "Nokia", 0x0421, "N95 Mobile Phone 8GB", 0x006e, DEVICE_FLAG_NONE },
+  // From Bastien Nocera <hadess@hadess.net>
+  { "Nokia", 0x0421, "N82 Mobile Phone", 0x0074, DEVICE_FLAG_NONE },
+  // From Martijn van de Streek <martijn@vandestreek.net>
+  { "Nokia", 0x0421, "N78 Mobile Phone", 0x0079, DEVICE_FLAG_NONE },
+  // From William Pettersson <the_enigma@users.sourceforge.net>
+  { "Nokia", 0x0421, "6220 Classic", 0x008d, DEVICE_FLAG_NONE },
+  // From kellerkev@gmail.com
+  { "Nokia", 0x0421, "N85 Mobile Phone", 0x0092, DEVICE_FLAG_NONE },
+  // From: danielw
+  { "Nokia", 0x0421, "E71", 0x00e4, DEVICE_FLAG_NONE },
+  // From: Laurent Bigonville <bigon@users.sourceforge.net>
+  { "Nokia", 0x0421, "E66", 0x00e5, DEVICE_FLAG_NONE },
+  // From: Pier <pierlucalino@users.sourceforge.net>
+  { "Nokia", 0x0421, "5320 XpressMusic", 0x00ea, DEVICE_FLAG_NONE },
+  // From: Gausie <innerdreams@users.sourceforge.net>
+  { "Nokia", 0x0421, "5800 XpressMusic", 0x0154, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // From: Willy Gardiol (web) <willy@gardiol.org>
+  // Spurious errors for getting all objects, lead me to believe this flag atleast is needed
+  { "Nokia", 0x0421, "5800 XpressMusic v2", 0x0155, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // From an anonymous SourceForge user
+  // Not verified to be MTP
+  { "Nokia", 0x0421, "E63", 0x0179, DEVICE_FLAG_NONE },
+  // From: FunkyPenguin <awafaa@users.sourceforge.net>
+  { "Nokia", 0x0421, "N97", 0x01f5, DEVICE_FLAG_NONE },
+  // From: Alexander Kojevnikov <alex-kay@users.sourceforge.net>
+  { "Nokia", 0x0421, "6600i", 0x0297, DEVICE_FLAG_NONE },
+  // https://sourceforge.net/tracker/index.php?func=detail&aid=2692473&group_id=8874&atid=358874
+  { "Nokia", 0x0421, "5200", 0x0421, DEVICE_FLAG_NONE },
+  // From: http://nds2.nokia.com/files/support/global/phones/software/Nokia_3250_WMP10_driver.inf
+  { "Nokia", 0x0421, "3250 Mobile Phone", 0x0462, DEVICE_FLAG_NONE },
+  // From http://nds2.nokia.com/files/support/global/phones/software/Nokia_N93_WMP10_Driver.inf
+  { "Nokia", 0x0421, "N93 Mobile Phone", 0x0478, DEVICE_FLAG_NONE },
+  // From: http://nds2.nokia.com/files/support/global/phones/software/Nokia_5500_Sport_WMP10_driver.inf
+  { "Nokia", 0x0421, "5500 Sport Mobile Phone", 0x047e, DEVICE_FLAG_NONE },
+  // From http://nds2.nokia.com/files/support/global/phones/software/Nokia_N91_WMP10_Driver.inf
+  { "Nokia", 0x0421, "N91 Mobile Phone", 0x0485, DEVICE_FLAG_NONE },
+  // From: Christian Rusa <kristous@users.sourceforge.net>
+  { "Nokia", 0x0421, "5700 XpressMusic Mobile Phone", 0x04b4, DEVICE_FLAG_NONE },
+  // From: Mitchell Hicks <mitchix@yahoo.com>
+  { "Nokia", 0x0421, "5300 Mobile Phone", 0x04ba, DEVICE_FLAG_NONE },
+  // From Christian Arnold <webmaster@arctic-media.de>
+  { "Nokia", 0x0421, "N73 Mobile Phone", 0x04d1, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Swapan <swapan@yahoo.com>
+  { "Nokia", 0x0421, "N75 Mobile Phone", 0x04e1, DEVICE_FLAG_NONE },
+  // From: http://nds2.nokia.com/files/support/global/phones/software/Nokia_N93i_WMP10_driver.inf
+  { "Nokia", 0x0421, "N93i Mobile Phone", 0x04e5, DEVICE_FLAG_NONE },
+  // From Anonymous Sourceforge User
+  { "Nokia", 0x0421, "N95 Mobile Phone", 0x04ef, DEVICE_FLAG_NONE },
+  // From: Pat Nicholls <pat@patandannie.co.uk>
+  { "Nokia", 0x0421, "N80 Internet Edition (Media Player)", 0x04f1, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Richard Wall <richard@the-moon.net>
+  { "Nokia", 0x05c6, "5530 Xpressmusic", 0x0229, DEVICE_FLAG_NONE },
+  // Reported by anonymous SourceForge user
+  // One thing stated by reporter (Nokia model) another by the detect log...
+  { "Nokia/Verizon", 0x05c6, "6205 Balboa/Verizon Music Phone", 0x3196, DEVICE_FLAG_NONE },
+
+
+  /*
+   * LOGIK
+   * Sold in the UK, seem to be manufactured by CCTech in China.
+   */
+  { "Logik", 0x13d1, "LOG DAX MP3 and DAB Player", 0x7002, DEVICE_FLAG_UNLOAD_DRIVER },
+
+  /*
+   * RCA / Thomson
+   */
+  // From kiki <omkiki@users.sourceforge.net>
+  { "Thomson", 0x069b, "EM28 Series", 0x0774, DEVICE_FLAG_NONE },
+  { "Thomson / RCA", 0x069b, "Opal / Lyra MC4002", 0x0777, DEVICE_FLAG_NONE },
+  { "Thomson", 0x069b, "Lyra MC5104B (M51 Series)", 0x077c, DEVICE_FLAG_NONE },
+  { "Thomson", 0x069b, "RCA H106", 0x301a, DEVICE_FLAG_UNLOAD_DRIVER },
+  // From Svenna <svenna@svenna.de>
+  // Not confirmed to be MTP.
+  { "Thomson", 0x069b, "scenium E308", 0x3028, DEVICE_FLAG_NONE },
+  // From XNJB user
+  { "Thomson / RCA", 0x069b, "Lyra HC308A", 0x3035, DEVICE_FLAG_NONE },
+  
+  /*
+   * NTT DoCoMo
+   */
+  { "FOMA", 0x04c5, "F903iX HIGH-SPEED", 0x1140, DEVICE_FLAG_NONE },
+
+  /*
+   * Palm device userland program named Pocket Tunes
+   * Reported by Peter Gyongyosi <gyp@impulzus.com>
+   */
+  { "NormSoft, Inc.", 0x1703, "Pocket Tunes", 0x0001, DEVICE_FLAG_NONE },
+  // Reported by anonymous submission
+  { "NormSoft, Inc.", 0x1703, "Pocket Tunes 4", 0x0002, DEVICE_FLAG_NONE },
+
+  /*
+   * TrekStor, Medion and Maxfield devices
+   * Their datasheet claims their devices are dualmode so probably needs to
+   * unload the attached drivers here.
+   */
+  // Reported by Stefan Voss <svoss@web.de>
+  // This is a Sigmatel SoC with a hard disk.
+  { "TrekStor", 0x066f, "Vibez 8/12GB", 0x842a,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by anonymous SourceForge user.
+  // This one done for Medion, whatever that is. Error reported so assume
+  // the same bug flag as its ancestor above.
+  { "Medion", 0x066f, "MD8333", 0x8550,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by anonymous SourceForge user
+  { "Medion", 0x066f, "MD8333", 0x8588,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by anonymous SourceForge user
+  { "Maxfield", 0x066f, "G-Flash NG 1GB", 0x846c,
+    DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Cristi Magherusan <majeru@gentoo.ro>
+  { "TrekStor", 0x0402, "i.Beat Sweez FM", 0x0611,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Fox-ino <fox-ino@users.sourceforge.net>
+  { "ALi Corp.", 0x0402, "MPMAN 2GB", 0x5668,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Anonymous SourceForge user
+  {"TrekStor", 0x1e68, "i.Beat Organix 2.0", 0x0002,
+    DEVICE_FLAG_UNLOAD_DRIVER },
+
+  /*
+   * Disney/Tevion/MyMusix
+   */
+  // Reported by XNJB user
+  { "Disney", 0x0aa6, "MixMax", 0x6021, DEVICE_FLAG_NONE },
+  // Reported by anonymous Sourceforge user 
+  { "Tevion", 0x0aa6, "MD 81488", 0x3011, DEVICE_FLAG_NONE },
+  // Reported by Peter Hedlund <peter@peterandlinda.com>
+  { "MyMusix", 0x0aa6, "PD-6070", 0x9601, DEVICE_FLAG_UNLOAD_DRIVER |
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST |
+    DEVICE_FLAG_NO_RELEASE_INTERFACE },
+
+  /*
+   * Cowon Systems, Inc.
+   * The iAudio audiophile devices don't encourage the use of MTP.
+   */
+  // Reported by Patrik Johansson <Patrik.Johansson@qivalue.com>
+  { "Cowon", 0x0e21, "iAudio U3 (MTP mode)", 0x0701,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Kevin Michael Smith <hai-etlik@users.sourceforge.net>
+  { "Cowon", 0x0e21, "iAudio 6 (MTP mode)", 0x0711,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Roberth Karman
+  { "Cowon", 0x0e21, "iAudio 7 (MTP mode)", 0x0751,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by an anonymous SourceForge user
+  { "Cowon", 0x0e21, "iAudio U5 (MTP mode)", 0x0761,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by TJ Something <tjbk_tjb@users.sourceforge.net>
+  { "Cowon", 0x0e21, "iAudio D2 (MTP mode)", 0x0801,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by anonymous Sourceforge user
+  { "Cowon", 0x0e21, "iAudio D2+ FW 2.x (MTP mode)", 0x0861,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // From Rockbox device listing
+  { "Cowon", 0x0e21, "iAudio D2+ DAB FW 4.x (MTP mode)", 0x0871,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // From Rockbox device listing
+  { "Cowon", 0x0e21, "iAudio D2+ FW 3.x (MTP mode)", 0x0881,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // From Rockbox device listing
+  { "Cowon", 0x0e21, "iAudio D2+ DMB FW 1.x (MTP mode)", 0x0891,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by <twkonefal@users.sourceforge.net>
+  { "Cowon", 0x0e21, "iAudio S9 (MTP mode)", 0x0901,
+   DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+   DEVICE_FLAG_OGG_IS_UNKNOWN | DEVICE_FLAG_FLAC_IS_UNKNOWN },
+
+  /*
+   * Insignia, dual-mode.
+   */
+  { "Insignia", 0x19ff, "NS-DV45", 0x0303, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by Rajan Bella <rajanbella@yahoo.com>
+  { "Insignia", 0x19ff, "Sport Player", 0x0307, DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by "brad" (anonymous, sourceforge)
+  { "Insignia", 0x19ff, "Pilot 4GB", 0x0309, DEVICE_FLAG_UNLOAD_DRIVER },
+
+  /*
+   * LG Electronics
+   */
+  // Not verified - anonymous submission
+  { "LG Electronics Inc.", 0x043e, "UP3", 0x70b1, DEVICE_FLAG_NONE },
+  // Reported by Joseph Nahmias <joe@nahimas.net>
+  { "LG Electronics Inc.", 0x1004, "VX8550 V CAST Mobile Phone", 0x6010,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR },
+  // Reported by Cyrille Potereau <cyrille.potereau@wanadoo.fr>
+  { "LG Electronics Inc.", 0x1004, "KC910 Renoir Mobile Phone", 0x608f,
+      DEVICE_FLAG_UNLOAD_DRIVER },
+  // Reported by anonymous sourceforge user
+  { "LG Electronics Inc.", 0x1004, "KM900", 0x6132,
+      DEVICE_FLAG_UNLOAD_DRIVER },
+
+  /*
+   * Sony
+   * It could be that these PIDs are one-per hundred series, so
+   * NWZ-A8xx is 0325, NWZ-S5xx is 0x326 etc. We need more devices
+   * reported to see a pattern here.
+   */
+  // Reported by Alessandro Radaelli <alessandro.radaelli@aruba.it>
+  { "Sony", 0x054c, "Walkman NWZ-A815/NWZ-A818", 0x0325,
+      DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by anonymous Sourceforge user.
+  { "Sony", 0x054c, "Walkman NWZ-S516", 0x0326, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Endre Oma <endre.88.oma@gmail.com>
+  { "Sony", 0x054c, "Walkman NWZ-S615F/NWZ-S616F/NWZ-S618F", 0x0327,
+      DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Jean-Marc Bourguet <jm@bourguet.org>
+  { "Sony", 0x054c, "Walkman NWZ-S716F", 0x035a, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Anon SF User / Anthon van der Neut <avanderneut@avid.com>
+  { "Sony", 0x054c, "Walkman NWZ-A826/NWZ-A828/NWZ-A829", 0x035b,
+      DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Niek Klaverstijn <niekez@users.sourceforge.net>
+  { "Sony", 0x054c, "Walkman NWZ-A726/NWZ-A728/NWZ-A768", 0x035c,
+      DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Mehdi AMINI <mehdi.amini - at - ulp.u-strasbg.fr>
+  { "Sony", 0x054c, "Walkman NWZ-B135", 0x036e, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by <tiagoboldt@users.sourceforge.net>
+  { "Sony", 0x054c, "Walkman NWZ-E436F", 0x0385, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Michael Wilkinson
+  { "Sony", 0x054c, "Walkman NWZ-W202", 0x0388, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Ondrej Sury <ondrej@sury.org>
+  { "Sony", 0x054c, "Walkman NWZ-S739F", 0x038c, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Marco Filipe Nunes Soares Abrantes Pereira <marcopereira@ua.pt>
+  { "Sony", 0x054c, "Walkman NWZ-S638F", 0x038e, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Elliot <orwells@users.sourceforge.net>
+  { "Sony", 0x054c, "Walkman NWZ-X1050B/NWZ-X1060B",
+    0x0397, DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Silvio J. Gutierrez <silviogutierrez@users.sourceforge.net>
+  { "Sony", 0x054c, "Walkman NWZ-X1051/NWZ-X1061", 0x0398,
+      DEVICE_FLAG_UNLOAD_DRIVER |
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+
+
+  /*
+   * SonyEricsson
+   * These initially seemed to support GetObjPropList but later revisions
+   * of the firmware seem to have broken it, so all are flagged as broken
+   * for now.
+   */
+  // Reported by Thomas Schweitzer <thomas_-_s@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "K550i", 0xe000, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Øyvind Stegard <stegaro@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "K850i", 0x0075, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Michael Eriksson
+  { "SonyEricsson", 0x0fce, "W910", 0x0076, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Zack <zackdvd@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "W890i", 0x00b3, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by robert dot ahlskog at gmail
+  { "SonyEricsson", 0x0fce, "W760i", 0x00c6, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Linus Åkesson <linusakesson@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "C902", 0x00d4, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by an anonymous SourceForge user
+  { "SonyEricsson", 0x0fce, "C702", 0x00d9, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Christian Zuckschwerdt <christian@zuckschwerdt.org>
+  { "SonyEricsson", 0x0fce, "W980", 0x00da, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by David Taylor <davidt-libmtp@yadt.co.uk>
+  { "SonyEricsson", 0x0fce, "C905", 0x00ef, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by David House <dmhouse@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "W595", 0x00f3,
+      DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
+  // Reported by Mattias Evensson <mevensson@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "W902", 0x00f5, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Sarunas <sarunas@users.sourceforge.net>
+  // Doesn't need any flags according to reporter
+  { "SonyEricsson", 0x0fce, "T700", 0x00fb, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by Stéphane Pontier <shadow_walker@users.sourceforge.net>
+  { "SonyEricsson", 0x0fce, "W705/W715", 0x0105, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+  // Reported by Håkan Kvist
+  { "SonyEricsson", 0x0fce, "W995", 0x0112, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+
+
+  /*
+   * Motorola
+   * Assume DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST on all of these.
+   */
+  // Reported by David Boyd <tiggrdave@users.sourceforge.net>
+  { "Motorola", 0x22b8, "V3m/V750 verizon", 0x2a65, DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST |
+    DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
+  // Reported by Marcus Meissner to libptp2
+  { "Motorola", 0x22b8, "K1", 0x4811, DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
+  // Reported by Hans-Joachim Baader <hjb@pro-linux.de> to libptp2
+  { "Motorola", 0x22b8, "A1200", 0x60ca, DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
+  // http://mark.cdmaforums.com/Files/Motdmmtp.inf
+  { "Motorola", 0x22b8, "MTP Test Command Interface", 0x6413, DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
+  // Reported by anonymous user
+  { "Motorola", 0x22b8, "RAZR2 V8/U9/Z6", 0x6415, DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
+  
+
+  /*
+   * Media Keg
+   */
+  // Reported by Rajan Bella <rajanbella@yahoo.com>
+  { "Kenwood", 0x0b28, "Media Keg HD10GB7 Sport Player", 0x100c, DEVICE_FLAG_UNLOAD_DRIVER},
+
+  /*
+   * Micro-Star International (MSI)
+   */
+  // Reported by anonymous sourceforge user.
+  { "Micro-Star International", 0x0db0, "P610/Model MS-5557", 0x5572, DEVICE_FLAG_NONE },
+
+  /*
+   * FOMA
+   */
+  { "FOMA", 0x06d3, "D905i", 0x21ba, DEVICE_FLAG_NONE },
+
+  /*
+   * Haier
+   */
+  // Both reported by an anonymous SourceForge user
+  // This is the 30 GiB model
+  { "Haier", 0x1302, "Ibiza Rhapsody", 0x1016, DEVICE_FLAG_NONE },
+  // This is the 4/8 GiB model
+  { "Haier", 0x1302, "Ibiza Rhapsody", 0x1017, DEVICE_FLAG_NONE },
+  
+  /*
+   * Panasonic
+   */
+  // Reported by dmizer
+  { "Panasonic", 0x04da, "P905i", 0x2145, DEVICE_FLAG_NONE },
+
+  /*
+   * Polaroid
+   */
+  { "Polaroid", 0x0546, "Freescape/MPU-433158", 0x2035, DEVICE_FLAG_NONE },
+
+  /*
+   * Pioneer
+   */
+  // Reported by Dan Allen <dan.j.allen@gmail.com>
+  { "Pioneer", 0x08e4, "XMP3", 0x0148, DEVICE_FLAG_NONE },
+
+  /*
+   * Slacker Inc.
+   * Put in all evilness flags because it looks fragile.
+   */
+  // Reported by Pug Fantus <pugfantus@users.sourceforge.net>
+  { "Slacker Inc.", 0x1bdc, "Slacker Portable Media Player", 0xfabf,
+    DEVICE_FLAG_BROKEN_BATTERY_LEVEL | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
+    DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST | DEVICE_FLAG_BROKEN_SEND_OBJECT_PROPLIST },
+
+  // Reported by Demadridsur <demadridsur@gmail.com>
+  { "O2 Sistemas", 0x1e53, "ZoltarTV", 0x0006, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
+
+  // Reported by Sense Hofstede <qense@users.sourceforge.net>
+  { "Perception Digital, Ltd", 0x0aa6, "Gigaware GX400", 0x9702, DEVICE_FLAG_NONE },
+  /*
+   * Other strange stuff.
+   */
+  { "Isabella", 0x0b20, "Her Prototype", 0xddee, DEVICE_FLAG_NONE }
diff --git a/src/playlist-spl.c b/src/playlist-spl.c
new file mode 100644
index 0000000..95844d0
--- /dev/null
+++ b/src/playlist-spl.c
@@ -0,0 +1,854 @@
+/**
+ * \File playlist-spl.c
+ *
+ * Playlist_t to Samsung (.spl) and back conversion functions.
+ *
+ * Copyright (C) 2008 Alistair Boyle <alistair.js.boyle@gmail.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h> // mkstmp()
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#include <fcntl.h>
+
+#include <string.h>
+
+#include "libmtp.h"
+#include "libusb-glue.h"
+#include "ptp.h"
+#include "unicode.h"
+
+#include "playlist-spl.h"
+
+// set this to 1 to add lots of messy debug output to the playlist code
+#define DEBUG_ENABLED 0
+
+// debug macro
+// d = indenting depth
+#define IF_DEBUG() if(DEBUG_ENABLED) {\
+                     printf("%s:%u:%s(): ", __FILE__, __LINE__, __func__); \
+                   } \
+                   if(DEBUG_ENABLED)
+
+// Internal singly linked list of strings
+// used to hold .spl playlist in memory
+typedef struct text_struct {
+  char* text; // String
+  struct text_struct *next; // Link to next line, NULL if end of list
+} text_t;
+
+
+/**
+ * Forward declarations of local (static) functions.
+ */
+static text_t* read_into_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd);
+static void write_from_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd, text_t* p);
+static void free_spl_text_t(text_t* p);
+static void print_spl_text_t(text_t* p);
+static uint32_t trackno_spl_text_t(text_t* p);
+static void tracks_from_spl_text_t(text_t* p, uint32_t* tracks, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
+static void spl_text_t_from_tracks(text_t** p, uint32_t* tracks, const uint32_t trackno, const uint32_t ver_major, const uint32_t ver_minor, char* dnse, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
+
+static uint32_t discover_id_from_filepath(const char* s, LIBMTP_folder_t* folders, LIBMTP_file_t* files); // TODO add file/dir cached args
+static void discover_filepath_from_id(char** p, uint32_t track, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
+static void find_folder_name(LIBMTP_folder_t* folders, uint32_t* id, char** name);
+static uint32_t find_folder_id(LIBMTP_folder_t* folders, uint32_t parent, char* name);
+
+static void append_text_t(text_t** t, char* s);
+
+
+
+
+/**
+ * Decides if the indicated object index is an .spl playlist.
+ *
+ * @param oi object we are deciding on
+ * @return 1 if this is a Samsung .spl object, 0 otherwise
+ */
+int is_spl_playlist(PTPObjectInfo *oi)
+{
+  return (oi->ObjectFormat == PTP_OFC_Undefined) &&
+         (strlen(oi->Filename) > 4) &&
+         (strcmp((oi->Filename + strlen(oi->Filename) -4), ".spl") == 0);
+}
+
+#ifndef HAVE_MKSTEMP
+# ifdef __WIN32__
+#  include <fcntl.h>
+#  define mkstemp(_pattern) _open(_mktemp(_pattern), _O_CREAT | _O_SHORT_LIVED | _O_EXCL)
+# else
+#  error Missing mkstemp() function.
+# endif
+#endif
+
+/**
+ * Take an object ID, a .spl playlist on the MTP device,
+ * and convert it to a playlist_t object.
+ *
+ * @param device mtp device pointer
+ * @param oi object we are reading
+ * @param id .spl playlist id on MTP device
+ * @param pl the LIBMTP_playlist_t pointer to be filled with info from id
+ */
+
+void spl_to_playlist_t(LIBMTP_mtpdevice_t* device, PTPObjectInfo *oi,
+                       const uint32_t id, LIBMTP_playlist_t * const pl)
+{
+  // Fill in playlist metadata
+  // Use the Filename as the playlist name, dropping the ".spl" extension
+  pl->name = malloc(sizeof(char)*(strlen(oi->Filename) -4 +1));
+  memcpy(pl->name, oi->Filename, strlen(oi->Filename) -4);
+  // Set terminating character
+  pl->name[strlen(oi->Filename) - 4] = 0;
+  pl->playlist_id = id;
+  pl->parent_id = oi->ParentObject;
+  pl->storage_id = oi->StorageID;
+  pl->tracks = NULL;
+  pl->no_tracks = 0;
+
+  IF_DEBUG() printf("pl->name='%s'\n",pl->name);
+
+  // open a temporary file
+  char tmpname[] = "/tmp/mtp-spl2pl-XXXXXX";
+  int fd = mkstemp(tmpname);
+  if(fd < 0) {
+    printf("failed to make temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
+    return;
+  }
+  // make sure the file will be deleted afterwards
+  if(unlink(tmpname) < 0)
+    printf("failed to delete temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
+  int ret = LIBMTP_Get_File_To_File_Descriptor(device, pl->playlist_id, fd, NULL, NULL);
+  if( ret < 0 ) {
+    // FIXME     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist: Could not get .spl playlist file.");
+    close(fd);
+    printf("FIXME closed\n");
+  }
+
+  text_t* p = read_into_spl_text_t(device, fd);
+  close(fd);
+
+  // FIXME cache these somewhere else so we don't keep calling this!
+  LIBMTP_folder_t *folders;
+  LIBMTP_file_t *files;
+  folders = LIBMTP_Get_Folder_List(device);
+  files = LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
+
+  // convert the playlist listing to track ids
+  pl->no_tracks = trackno_spl_text_t(p);
+  IF_DEBUG() printf("%u track%s found\n", pl->no_tracks, pl->no_tracks==1?"":"s");
+  pl->tracks = malloc(sizeof(uint32_t)*(pl->no_tracks));
+  tracks_from_spl_text_t(p, pl->tracks, folders, files);
+
+  free_spl_text_t(p);
+
+  // debug: add a break since this is the top level function call
+  IF_DEBUG() printf("------------\n\n");
+}
+
+
+/**
+ * Push a playlist_t onto the device after converting it to a .spl format
+ *
+ * @param device mtp device pointer
+ * @param pl the LIBMTP_playlist_t to convert (pl->playlist_id will be updated
+ *           with the newly created object's id)
+ * @return 0 on success, any other value means failure.
+ */
+int playlist_t_to_spl(LIBMTP_mtpdevice_t *device,
+                      LIBMTP_playlist_t * const pl)
+{
+  text_t* t;
+  LIBMTP_folder_t *folders;
+  LIBMTP_file_t *files;
+  folders = LIBMTP_Get_Folder_List(device);
+  files = LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
+
+  char tmpname[] = "/tmp/mtp-spl2pl-XXXXXX"; // must be a var since mkstemp modifies it
+
+  IF_DEBUG() printf("pl->name='%s'\n",pl->name);
+
+  // open a file descriptor
+  int fd = mkstemp(tmpname);
+  if(fd < 0) {
+    printf("failed to make temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
+    return -1;
+  }
+  // make sure the file will be deleted afterwards
+  if(unlink(tmpname) < 0)
+    printf("failed to delete temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
+
+  // decide on which version of the .spl format to use
+  uint32_t ver_major;
+  uint32_t ver_minor = 0;
+  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+  if(FLAG_PLAYLIST_SPL_V2(ptp_usb)) ver_major = 2;
+  else ver_major = 1; // FLAG_PLAYLIST_SPL_V1()
+
+  IF_DEBUG() printf("%u track%s\n", pl->no_tracks, pl->no_tracks==1?"":"s");
+  IF_DEBUG() printf(".spl version %d.%02d\n", ver_major, ver_minor);
+
+  // create the text for the playlist
+  spl_text_t_from_tracks(&t, pl->tracks, pl->no_tracks, ver_major, ver_minor, NULL, folders, files);
+  write_from_spl_text_t(device, fd, t);
+  free_spl_text_t(t); // done with the text
+
+  // create the file object for storing
+  LIBMTP_file_t* f = malloc(sizeof(LIBMTP_file_t));
+  f->item_id = 0;
+  f->parent_id = pl->parent_id;
+  f->storage_id = pl->storage_id;
+  f->filename = malloc(sizeof(char)*(strlen(pl->name)+5));
+  strcpy(f->filename, pl->name);
+  strcat(f->filename, ".spl"); // append suffix
+  f->filesize = lseek(fd, 0, SEEK_CUR); // file desc is currently at end of file
+  f->filetype = LIBMTP_FILETYPE_UNKNOWN;
+  f->next = NULL;
+
+  IF_DEBUG() printf("%s is %dB\n", f->filename, (int)f->filesize);
+
+  // push the playlist to the device
+  lseek(fd, 0, SEEK_SET); // reset file desc. to start of file
+  int ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, f, NULL, NULL);
+  pl->playlist_id = f->item_id;
+  free(f->filename);
+  free(f);
+
+  // release the memory when we're done with it
+  close(fd);
+  // debug: add a break since this is the top level function call
+  IF_DEBUG() printf("------------\n\n");
+
+  return ret;
+}
+
+
+
+/**
+ * Update a playlist on the device. If only the playlist's name is being
+ * changed the pl->playlist_id will likely remain the same. An updated track
+ * list will result in the old playlist being replaced (ie: new playlist_id).
+ * NOTE: Other playlist metadata aside from playlist name and tracks are
+ * ignored.
+ *
+ * @param device mtp device pointer
+ * @param new the LIBMTP_playlist_t to convert (pl->playlist_id will be updated
+ *           with the newly created object's id)
+ * @return 0 on success, any other value means failure.
+ */
+int update_spl_playlist(LIBMTP_mtpdevice_t *device,
+			  LIBMTP_playlist_t * const newlist)
+{
+  IF_DEBUG() printf("pl->name='%s'\n",newlist->name);
+
+  // read in the playlist of interest
+  LIBMTP_playlist_t * old = LIBMTP_Get_Playlist(device, newlist->playlist_id);
+  
+  // check to see if we found it
+  if (!old)
+    return -1;
+
+  // check if the playlists match
+  int delta = 0;
+  int i;
+  if(old->no_tracks != newlist->no_tracks)
+    delta++;
+  for(i=0;i<newlist->no_tracks && delta==0;i++) {
+    if(old->tracks[i] != newlist->tracks[i])
+      delta++;
+  }
+
+  // if not, kill the playlist and replace it
+  if(delta) {
+    IF_DEBUG() printf("new tracks detected:\n");
+    IF_DEBUG() printf("delete old playlist and build a new one\n");
+    IF_DEBUG() printf(" NOTE: new playlist_id will result!\n");
+    if(LIBMTP_Delete_Object(device, old->playlist_id) != 0)
+      return -1;
+
+    IF_DEBUG() {
+      if(strcmp(old->name,newlist->name) == 0)
+        printf("name unchanged\n");
+      else
+        printf("name is changing too -> %s\n",newlist->name);
+    }
+
+    return LIBMTP_Create_New_Playlist(device, newlist);
+  }
+
+
+  // update the name only
+  if(strcmp(old->name,newlist->name) != 0) {
+    IF_DEBUG() printf("ONLY name is changing -> %s\n",newlist->name);
+    IF_DEBUG() printf("playlist_id will remain unchanged\n");
+    char* s = malloc(sizeof(char)*(strlen(newlist->name)+5));
+    strcpy(s, newlist->name);
+    strcat(s,".spl"); // FIXME check for success
+    int ret = LIBMTP_Set_Playlist_Name(device, newlist, s);
+    free(s);
+    return ret;
+  }
+
+  IF_DEBUG() printf("no change\n");
+  return 0; // nothing to be done, success
+}
+
+
+/**
+ * Load a file descriptor into a string.
+ *
+ * @param device a pointer to the current device.
+ *               (needed for ucs2->utf8 charset conversion)
+ * @param fd the file descriptor to load
+ * @return text_t* a linked list of lines of text, id is left blank, NULL if nothing read in
+ */
+static text_t* read_into_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd)
+{
+  // set MAXREAD to match STRING_BUFFER_LENGTH in unicode.h conversion function
+  const size_t MAXREAD = 1024*2;
+  char t[MAXREAD];
+  // upto 3 bytes per utf8 character, 2 bytes per ucs2 character,
+  // +1 for '\0' at end of string
+  const size_t WSIZE = MAXREAD/2*3+1;
+  char w[WSIZE];
+  char* it = t; // iterator on t
+  char* iw = w;
+  ssize_t rdcnt;
+  off_t offcnt;
+  text_t* head = NULL;
+  text_t* tail = NULL;
+  int eof = 0;
+
+  // reset file descriptor (fd) to start of file
+  offcnt = lseek(fd, 0, SEEK_SET);
+
+  while(!eof) {
+    // find the current offset in the file
+    // to allow us to determine how many bytes we read if we hit the EOF
+    // where returned rdcnt=0 from read()
+    offcnt = lseek(fd, 0, SEEK_CUR);
+    // read to refill buffer
+    // (there might be data left from an incomplete last string in t,
+    // hence start filling at it)
+    it = t; // set ptr to start of buffer
+    rdcnt = read(fd, it, sizeof(char)*MAXREAD);
+    if(rdcnt < 0)
+      printf("load_spl_fd read err %s\n", strerror(errno));
+    else if(rdcnt == 0) { // for EOF, fix rdcnt
+      if(it-t == MAXREAD)
+        printf("error -- buffer too small to read in .spl playlist entry\n");
+
+      rdcnt = lseek(fd, 0, SEEK_CUR) - offcnt;
+      eof = 1;
+    }
+
+    IF_DEBUG() printf("read buff= {%dB new, %dB old/left-over}%s\n",(int)rdcnt, (int)(iw-w), eof?", EOF":"");
+
+    // while more input bytes
+    char* it_end = t + rdcnt;
+    while(it < it_end) {
+      // copy byte, unless EOL (then replace with end-of-string \0)
+      if(*it == '\r' || *it == '\n')
+        *iw = '\0';
+      else
+        *iw = *it;
+
+      it++;
+      iw++;
+
+      // EOL -- store it
+      if( (iw-w) >= 2 && // we must have at least two bytes
+          *(iw-1) == '\0' && *(iw-2) == '\0' && // 0x0000 is end-of-string
+          // but it must be aligned such that we have an {odd,even} set of
+          // bytes since we are expecting to consume bytes two-at-a-time
+          !((iw-w)%2) ) {
+
+        // drop empty lines
+        //  ... cast as a string of 2 byte characters
+        if(ucs2_strlen((uint16_t*)w) == 0) {
+          iw = w;
+          continue;
+        }
+
+        // create a new node in the list
+        if(head == NULL) {
+          head = malloc(sizeof(text_t));
+          tail = head;
+        }
+        else {
+          tail->next = malloc(sizeof(text_t));
+          tail = tail->next;
+        }
+        // fill in the data for the node
+        //  ... cast as a string of 2 byte characters
+        tail->text = utf16_to_utf8(device, (uint16_t*) w);
+        iw = w; // start again
+
+        IF_DEBUG() printf("line: %s\n", tail->text);
+      }
+
+      // prevent buffer overflow
+      if(iw >= w + WSIZE) {
+        // if we ever see this error its BAD:
+        //   we are dropping all the processed bytes for this line and
+        //   proceeding on as if everything is okay, probably losing a track
+        //   from the playlist
+        printf("ERROR %s:%u:%s(): buffer overflow! .spl line too long @ %zuB\n",
+               __FILE__, __LINE__, __func__, WSIZE);
+        iw = w; // reset buffer
+      }
+    }
+
+    // if the last thing we did was save our line, then we finished working
+    // on the input buffer and we can start fresh
+    // otherwise we need to save our partial work, if we're not quiting (eof).
+    // there is nothing special we need to do, to achieve this since the
+    // partially completed string will sit in 'w' until we return to complete
+    // the line
+
+  }
+
+  // set the next pointer at the end
+  // if there is any list
+  if(head != NULL)
+    tail->next = NULL;
+
+  // return the head of the list (NULL if no list)
+  return head;
+}
+
+
+/**
+ * Write a .spl text file to a file in preparation for pushing it
+ * to the device.
+ *
+ * @param fd file descriptor to write to
+ * @param p the text to output one line per string in the linked list
+ * @see playlist_t_to_spl()
+ */
+static void write_from_spl_text_t(LIBMTP_mtpdevice_t *device,
+                                  const int fd,
+                                  text_t* p) {
+  ssize_t ret;
+  // write out BOM for utf16/ucs2 (byte order mark)
+  ret = write(fd,"\xff\xfe",2);
+  while(p != NULL) {
+    char *const t = (char*) utf8_to_utf16(device, p->text);
+    // note: 2 bytes per ucs2 character
+    const size_t len = ucs2_strlen((uint16_t*)t)*sizeof(uint16_t);
+    int i;
+
+    IF_DEBUG() {
+      printf("\nutf8=%s ",p->text);
+      for(i=0;i<strlen(p->text);i++)
+        printf("%02x ", p->text[i] & 0xff);
+      printf("\n");
+      printf("ucs2=");
+      for(i=0;i<ucs2_strlen((uint16_t*)t)*sizeof(uint16_t);i++)
+        printf("%02x ", t[i] & 0xff);
+      printf("\n");
+    }
+
+    // write: utf8 -> utf16
+    ret += write(fd, t, len);
+
+    // release the converted string
+    free(t);
+
+    // check for failures
+    if(ret < 0)
+      printf("write spl file failed: %s\n", strerror(errno));
+    else if(ret != len +2)
+      printf("write spl file wrong number of bytes ret=%d len=%d '%s'\n", (int)ret, (int)len, p->text);
+
+    // write carriage return, line feed in ucs2
+    ret = write(fd, "\r\0\n\0", 4);
+    if(ret < 0)
+      printf("write spl file failed: %s\n", strerror(errno));
+    else if(ret != 4)
+      printf("failed to write the correct number of bytes '\\n'!\n");
+
+    // fake out count (first time through has two extra bytes from BOM)
+    ret = 2;
+
+    // advance to the next line
+    p = p->next;
+  }
+}
+
+/**
+ * Destroy a linked-list of strings.
+ *
+ * @param p the list to destroy
+ * @see spl_to_playlist_t()
+ * @see playlist_t_to_spl()
+ */
+static void free_spl_text_t(text_t* p)
+{
+  text_t* d;
+  while(p != NULL) {
+    d = p;
+    free(p->text);
+    p = p->next;
+    free(d);
+  }
+}
+
+/**
+ * Print a linked-list of strings to stdout.
+ *
+ * @param p the list to print
+ */
+static void print_spl_text_t(text_t* p)
+{
+  while(p != NULL) {
+    printf("%s\n",p->text);
+    p = p->next;
+  }
+}
+
+/**
+ * Count the number of tracks in this playlist. A track will be counted as
+ * such if the line starts with a leading slash.
+ *
+ * @param p the text to search
+ * @return number of tracks in the playlist
+ * @see spl_to_playlist_t()
+ */
+static uint32_t trackno_spl_text_t(text_t* p) {
+  uint32_t c = 0;
+  while(p != NULL) {
+    if(p->text[0] == '\\' ) c++;
+    p = p->next;
+  }
+
+  return c;
+}
+
+/**
+ * Find the track ids for this playlist's files.
+ * (ie: \Music\song.mp3 -> 12345)
+ *
+ * @param p the text to search
+ * @param tracks returned list of track id's for the playlist_t, must be large
+ *               enough to accomodate all the tracks as reported by
+ *               trackno_spl_text_t()
+ * @param folders the folders list for the device
+ * @param fiels the files list for the device
+ * @see spl_to_playlist_t()
+ */
+static void tracks_from_spl_text_t(text_t* p,
+                                   uint32_t* tracks,
+                                   LIBMTP_folder_t* folders,
+                                   LIBMTP_file_t* files)
+{
+  uint32_t c = 0;
+  while(p != NULL) {
+    if(p->text[0] == '\\' ) {
+      tracks[c] = discover_id_from_filepath(p->text, folders, files);
+      IF_DEBUG()
+        printf("track %d = %s (%u)\n", c+1, p->text, tracks[c]);
+      c++;
+    }
+    p = p->next;
+  }
+}
+
+
+/**
+ * Find the track names (including path) for this playlist's track ids.
+ * (ie: 12345 -> \Music\song.mp3)
+ *
+ * @param p the text to search
+ * @param tracks list of track id's to look up
+ * @param folders the folders list for the device
+ * @param fiels the files list for the device
+ * @see playlist_t_to_spl()
+ */
+static void spl_text_t_from_tracks(text_t** p,
+                                   uint32_t* tracks,
+                                   const uint32_t trackno,
+                                   const uint32_t ver_major,
+                                   const uint32_t ver_minor,
+                                   char* dnse,
+                                   LIBMTP_folder_t* folders,
+                                   LIBMTP_file_t* files)
+{
+
+  // HEADER
+  text_t* c = NULL;
+  append_text_t(&c, "SPL PLAYLIST");
+  *p = c; // save the top of the list!
+
+  char vs[14]; // "VERSION 2.00\0"
+  sprintf(vs,"VERSION %d.%02d",ver_major,ver_minor);
+
+  append_text_t(&c, vs);
+  append_text_t(&c, "");
+
+  // TRACKS
+  int i;
+  char* f;
+  for(i=0;i<trackno;i++) {
+    discover_filepath_from_id(&f, tracks[i], folders, files);
+
+    if(f != NULL) {
+      append_text_t(&c, f);
+      IF_DEBUG()
+        printf("track %d = %s (%u)\n", i+1, f, tracks[i]);
+    }
+    else
+      printf("failed to find filepath for track=%d\n", tracks[i]);
+  }
+
+  // FOOTER
+  append_text_t(&c, "");
+  append_text_t(&c, "END PLAYLIST");
+  if(ver_major == 2) {
+    append_text_t(&c, "");
+    append_text_t(&c, "myDNSe DATA");
+    if(dnse != NULL) {
+      append_text_t(&c, dnse);
+    }
+    else {
+      append_text_t(&c, "");
+      append_text_t(&c, "");
+    }
+    append_text_t(&c, "END myDNSe");
+  }
+
+  c->next = NULL;
+
+  // debug
+  IF_DEBUG() {
+    printf(".spl playlist:\n");
+    print_spl_text_t(*p);
+  }
+}
+
+
+/**
+ * Find the track names (including path) given a fileid
+ * (ie: 12345 -> \Music\song.mp3)
+ *
+ * @param p returns the file path (ie: \Music\song.mp3),
+ *          (*p) == NULL if the look up fails
+ * @param track track id to look up
+ * @param folders the folders list for the device
+ * @param files the files list for the device
+ * @see spl_text_t_from_tracks()
+ */
+
+// returns p = NULL on failure, else the filepath to the track including track name, allocated as a correct length string
+static void discover_filepath_from_id(char** p,
+                                      uint32_t track,
+                                      LIBMTP_folder_t* folders,
+                                      LIBMTP_file_t* files)
+{
+  // fill in a string from the right side since we don't know the root till the end
+  const int M = 1024;
+  char w[M];
+  char* iw = w + M; // iterator on w
+
+  // in case of failure return NULL string
+  *p = NULL;
+
+
+  // find the right file
+  while(files != NULL && files->item_id != track) {
+    files = files->next;
+  }
+  // if we didn't find a matching file, abort
+  if(files == NULL)
+    return;
+
+  // stuff the filename into our string
+  // FIXME: check for string overflow before it occurs
+  iw = iw - (strlen(files->filename) +1); // leave room for '\0' at the end
+  strcpy(iw,files->filename);
+
+  // next follow the directories to the root
+  // prepending folders to the path as we go
+  uint32_t id = files->parent_id;
+  char* f = NULL;
+  while(id != 0) {
+    find_folder_name(folders, &id, &f);
+    if(f == NULL) return; // fail if the next part of the path couldn't be found
+    iw = iw - (strlen(f) +1);
+    // FIXME: check for string overflow before it occurs
+    strcpy(iw, f);
+    iw[strlen(f)] = '\\';
+    free(f);
+  }
+
+  // prepend a slash
+  iw--;
+  iw[0] = '\\';
+
+  // now allocate a string of the right length to be returned
+  *p = strdup(iw);
+}
+
+
+/**
+ * Find the track id given a track's name (including path)
+ * (ie: \Music\song.mp3 -> 12345)
+ *
+ * @param s file path to look up (ie: \Music\song.mp3),
+ *          (*p) == NULL if the look up fails
+ * @param folders the folders list for the device
+ * @param files the files list for the device
+ * @return track id, 0 means failure
+ * @see tracks_from_spl_text_t()
+ */
+static uint32_t discover_id_from_filepath(const char* s, LIBMTP_folder_t* folders, LIBMTP_file_t* files)
+{
+  // abort if this isn't a path
+  if(s[0] != '\\')
+    return 0;
+
+  int i;
+  uint32_t id = 0;
+  char* sc = strdup(s);
+  char* sci = sc +1; // iterator
+  // skip leading slash in path
+
+  // convert all \ to \0
+  size_t len = strlen(s);
+  for(i=0;i<len;i++) {
+    if(sc[i] == '\\') {
+      sc[i] = '\0';
+    }
+  }
+
+  // now for each part of the string, find the id
+  while(sci != sc + len +1) {
+    // if its the last part of the string, its the filename
+    if(sci + strlen(sci) == sc + len) {
+
+      while(files != NULL) {
+        // check parent matches id and name matches sci
+        if( (files->parent_id == id) &&
+            (strcmp(files->filename, sci) == 0) ) { // found it!
+          id = files->item_id;
+          break;
+        }
+        files = files->next;
+      }
+    }
+    else { // otherwise its part of the directory path
+      id = find_folder_id(folders, id, sci);
+    }
+
+    // move to next folder/file
+    sci += strlen(sci) +1;
+  }
+
+  // release our copied string
+  free(sc);
+
+  // FIXME check that we actually have a file
+
+  return id;
+}
+
+
+
+/**
+ * Find the folder name given the folder's id.
+ *
+ * @param folders the folders list for the device
+ * @param id the folder_id to look up, returns the folder's parent folder_id
+ * @param name returns the name of the folder or NULL on failure
+ * @see discover_filepath_from_id()
+ */
+static void find_folder_name(LIBMTP_folder_t* folders, uint32_t* id, char** name)
+{
+
+  // FIXME this function is exactly LIBMTP_Find_Folder
+
+  LIBMTP_folder_t* f = LIBMTP_Find_Folder(folders, *id);
+  if(f == NULL) {
+    *name = NULL;
+  }
+  else { // found it!
+    *name = strdup(f->name);
+    *id = f->parent_id;
+  }
+}
+
+
+/**
+ * Find the folder id given the folder's name and parent id.
+ *
+ * @param folders the folders list for the device
+ * @param parent the folder's parent's id
+ * @param name the name of the folder
+ * @return the folder_id or 0 on failure
+ * @see discover_filepath_from_id()
+ */
+static uint32_t find_folder_id(LIBMTP_folder_t* folders, uint32_t parent, char* name) {
+
+  if(folders == NULL)
+    return 0;
+
+  // found it!
+  else if( (folders->parent_id == parent) &&
+           (strcmp(folders->name, name) == 0) )
+    return folders->folder_id;
+
+  // no luck so far, search both siblings and children
+  else {
+    uint32_t id = 0;
+
+    if(folders->sibling != NULL)
+      id = find_folder_id(folders->sibling, parent, name);
+    if( (id == 0) && (folders->child != NULL) )
+      id = find_folder_id(folders->child, parent, name);
+
+    return id;
+  }
+}
+
+
+/**
+ * Append a string to a linked-list of strings.
+ *
+ * @param t the list-of-strings, returns with the added string
+ * @param s the string to append
+ * @see spl_text_t_from_tracks()
+ */
+static void append_text_t(text_t** t, char* s)
+{
+  if(*t == NULL) {
+    *t = malloc(sizeof(text_t));
+  }
+  else {
+    (*t)->next = malloc(sizeof(text_t));
+    (*t) = (*t)->next;
+  }
+  (*t)->text = strdup(s);
+}
diff --git a/src/playlist-spl.h b/src/playlist-spl.h
new file mode 100644
index 0000000..ee00d35
--- /dev/null
+++ b/src/playlist-spl.h
@@ -0,0 +1,35 @@
+/*
+ * \file playlist-spl.h
+ * Playlist to .spl conversion functions.
+ *
+ * Copyright (C) 2008 Alistair Boyle <alistair.js.boyle@gmail.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MTP__PLAYLIST_SPL__H
+#define __MTP__PLAYLIST_SPL__H
+
+int is_spl_playlist(PTPObjectInfo *oi);
+
+void spl_to_playlist_t(LIBMTP_mtpdevice_t* device, PTPObjectInfo *oi,
+                       const uint32_t id, LIBMTP_playlist_t * const pl);
+int playlist_t_to_spl(LIBMTP_mtpdevice_t *device,
+                      LIBMTP_playlist_t * const metadata);
+int update_spl_playlist(LIBMTP_mtpdevice_t *device,
+			  LIBMTP_playlist_t * const newlist);
+
+#endif //__MTP__PLAYLIST_SPL__H
diff --git a/src/ptp-pack.c b/src/ptp-pack.c
new file mode 100644
index 0000000..c6a090c
--- /dev/null
+++ b/src/ptp-pack.c
@@ -0,0 +1,1877 @@
+/* currently this file is included into ptp.c */
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+extern void
+ptp_debug (PTPParams *params, const char *format, ...);
+
+static inline uint16_t
+htod16p (PTPParams *params, uint16_t var)
+{
+	return ((params->byteorder==PTP_DL_LE)?htole16(var):htobe16(var));
+}
+
+static inline uint32_t
+htod32p (PTPParams *params, uint32_t var)
+{
+	return ((params->byteorder==PTP_DL_LE)?htole32(var):htobe32(var));
+}
+
+static inline void
+htod16ap (PTPParams *params, unsigned char *a, uint16_t val)
+{
+	if (params->byteorder==PTP_DL_LE)
+		htole16a(a,val);
+	else 
+		htobe16a(a,val);
+}
+
+static inline void
+htod32ap (PTPParams *params, unsigned char *a, uint32_t val)
+{
+	if (params->byteorder==PTP_DL_LE)
+		htole32a(a,val);
+	else 
+		htobe32a(a,val);
+}
+
+static inline void
+htod64ap (PTPParams *params, unsigned char *a, uint64_t val)
+{
+	if (params->byteorder==PTP_DL_LE)
+		htole64a(a,val);
+	else 
+		htobe64a(a,val);
+}
+
+static inline uint16_t
+dtoh16p (PTPParams *params, uint16_t var)
+{
+	return ((params->byteorder==PTP_DL_LE)?le16toh(var):be16toh(var));
+}
+
+static inline uint32_t
+dtoh32p (PTPParams *params, uint32_t var)
+{
+	return ((params->byteorder==PTP_DL_LE)?le32toh(var):be32toh(var));
+}
+
+static inline uint64_t
+dtoh64p (PTPParams *params, uint64_t var)
+{
+	return ((params->byteorder==PTP_DL_LE)?le64toh(var):be64toh(var));
+}
+
+static inline uint16_t
+dtoh16ap (PTPParams *params, const unsigned char *a)
+{
+	return ((params->byteorder==PTP_DL_LE)?le16atoh(a):be16atoh(a));
+}
+
+static inline uint32_t
+dtoh32ap (PTPParams *params, const unsigned char *a)
+{
+	return ((params->byteorder==PTP_DL_LE)?le32atoh(a):be32atoh(a));
+}
+
+static inline uint64_t
+dtoh64ap (PTPParams *params, const unsigned char *a)
+{
+	return ((params->byteorder==PTP_DL_LE)?le64atoh(a):be64atoh(a));
+}
+
+#define htod8a(a,x)	*(uint8_t*)(a) = x
+#define htod16a(a,x)	htod16ap(params,a,x)
+#define htod32a(a,x)	htod32ap(params,a,x)
+#define htod64a(a,x)	htod64ap(params,a,x)
+#define htod16(x)	htod16p(params,x)
+#define htod32(x)	htod32p(params,x)
+#define htod64(x)	htod64p(params,x)
+
+#define dtoh8a(x)	(*(uint8_t*)(x))
+#define dtoh16a(a)	dtoh16ap(params,a)
+#define dtoh32a(a)	dtoh32ap(params,a)
+#define dtoh64a(a)	dtoh64ap(params,a)
+#define dtoh16(x)	dtoh16p(params,x)
+#define dtoh32(x)	dtoh32p(params,x)
+#define dtoh64(x)	dtoh64p(params,x)
+
+
+static inline char*
+ptp_unpack_string(PTPParams *params, unsigned char* data, uint16_t offset, uint8_t *len)
+{
+	uint8_t length;
+	uint16_t string[PTP_MAXSTRLEN+1];
+	/* allow for UTF-8: max of 3 bytes per UCS-2 char, plus final null */
+	char loclstr[PTP_MAXSTRLEN*3+1]; 
+	size_t nconv, srclen, destlen;
+	char *src, *dest;
+
+	length = dtoh8a(&data[offset]);	/* PTP_MAXSTRLEN == 255, 8 bit len */
+	*len = length;
+	if (length == 0)		/* nothing to do? */
+		return(NULL);
+
+	/* copy to string[] to ensure correct alignment for iconv(3) */
+	memcpy(string, &data[offset+1], length * sizeof(string[0]));
+	string[length] = 0x0000U;   /* be paranoid!  add a terminator. */
+	loclstr[0] = '\0';
+    
+	/* convert from camera UCS-2 to our locale */
+	src = (char *)string;
+	srclen = length * sizeof(string[0]);
+	dest = loclstr;
+	destlen = sizeof(loclstr)-1;
+	nconv = (size_t)-1;
+#ifdef HAVE_ICONV
+	nconv = iconv(params->cd_ucs2_to_locale, &src, &srclen, 
+			&dest, &destlen);
+#endif
+	if (nconv == (size_t) -1) { /* do it the hard way */
+		int i;
+		/* try the old way, in case iconv is broken */
+		for (i=0;i<length;i++) {
+			if (dtoh16a(&data[offset+1+2*i])>127)
+				loclstr[i] = '?';
+			else
+				loclstr[i] = dtoh16a(&data[offset+1+2*i]);
+		}
+		dest = loclstr+length;
+	}
+	*dest = '\0';
+	loclstr[sizeof(loclstr)-1] = '\0';   /* be safe? */
+	return(strdup(loclstr));
+}
+
+static inline int
+ucs2strlen(uint16_t const * const unicstr)
+{
+	int length;
+	
+	/* Unicode strings are terminated with 2 * 0x00 */
+	for(length = 0; unicstr[length] != 0x0000U; length ++);
+	return length;
+}
+
+
+static inline void
+ptp_pack_string(PTPParams *params, char *string, unsigned char* data, uint16_t offset, uint8_t *len)
+{
+	int packedlen;
+	uint16_t ucs2str[PTP_MAXSTRLEN+1];
+	char *ucs2strp = (char *) ucs2str;
+	size_t convlen = strlen(string);
+
+	/* Cannot exceed 255 (PTP_MAXSTRLEN) since it is a single byte, duh ... */
+	memset(ucs2strp, 0, sizeof(ucs2str));  /* XXX: necessary? */
+#ifdef HAVE_ICONV
+	{ 
+		size_t nconv;
+		size_t convmax = PTP_MAXSTRLEN * 2; /* Includes the terminator */
+		char *stringp = string;
+
+		nconv = iconv(params->cd_locale_to_ucs2, &stringp, &convlen,
+			&ucs2strp, &convmax);
+		if (nconv == (size_t) -1)
+			ucs2str[0] = 0x0000U;
+	}
+#else
+	{
+		int i;
+		for (i=0;i<convlen;i++) {
+			ucs2str[i] = string[i];
+		}
+		ucs2str[convlen] = 0;
+	}
+#endif
+	/*
+	 * XXX: isn't packedlen just ( (uint16_t *)ucs2strp - ucs2str )?
+	 *      why do we need ucs2strlen()?
+	 */
+	packedlen = ucs2strlen(ucs2str);
+	if (packedlen > PTP_MAXSTRLEN-1) {
+		*len=0;
+		return;
+	}
+	
+	/* number of characters including terminating 0 (PTP standard confirmed) */
+	htod8a(&data[offset],packedlen+1);
+	memcpy(&data[offset+1], &ucs2str[0], packedlen * sizeof(ucs2str[0]));
+	htod16a(&data[offset+packedlen*2+1], 0x0000);  /* terminate 0 */
+
+	/* The returned length is in number of characters */
+	*len = (uint8_t) packedlen+1;
+}
+
+static inline unsigned char *
+ptp_get_packed_stringcopy(PTPParams *params, char *string, uint32_t *packed_size)
+{
+	uint8_t packed[PTP_MAXSTRLEN*2+3], len;
+	size_t plen;
+	unsigned char *retcopy = NULL;
+  
+	if (string == NULL)
+	  ptp_pack_string(params, "", (unsigned char*) packed, 0, &len);
+	else
+	  ptp_pack_string(params, string, (unsigned char*) packed, 0, &len);
+  
+	/* returned length is in characters, then one byte for string length */
+	plen = len*2 + 1;
+	
+	retcopy = malloc(plen);
+	if (!retcopy) {
+		*packed_size = 0;
+		return NULL;
+	}
+	memcpy(retcopy, packed, plen);
+	*packed_size = plen;
+	return (retcopy);
+}
+
+static inline uint32_t
+ptp_unpack_uint32_t_array(PTPParams *params, unsigned char* data, uint16_t offset, uint32_t **array)
+{
+	uint32_t n, i=0;
+
+	n=dtoh32a(&data[offset]);
+	*array = malloc (n*sizeof(uint32_t));
+	while (n>i) {
+		(*array)[i]=dtoh32a(&data[offset+(sizeof(uint32_t)*(i+1))]);
+		i++;
+	}
+	return n;
+}
+
+static inline uint32_t
+ptp_pack_uint32_t_array(PTPParams *params, uint32_t *array, uint32_t arraylen, unsigned char **data )
+{
+	uint32_t i=0;
+
+	*data = malloc ((arraylen+1)*sizeof(uint32_t));
+	htod32a(&(*data)[0],arraylen);
+	for (i=0;i<arraylen;i++)
+		htod32a(&(*data)[sizeof(uint32_t)*(i+1)], array[i]);
+	return (arraylen+1)*sizeof(uint32_t);
+}
+
+static inline uint32_t
+ptp_unpack_uint16_t_array(PTPParams *params, unsigned char* data, uint16_t offset, uint16_t **array)
+{
+	uint32_t n, i=0;
+
+	n=dtoh32a(&data[offset]);
+	*array = malloc (n*sizeof(uint16_t));
+	while (n>i) {
+		(*array)[i]=dtoh16a(&data[offset+(sizeof(uint16_t)*(i+2))]);
+		i++;
+	}
+	return n;
+}
+
+/* DeviceInfo pack/unpack */
+
+#define PTP_di_StandardVersion		 0
+#define PTP_di_VendorExtensionID	 2
+#define PTP_di_VendorExtensionVersion	 6
+#define PTP_di_VendorExtensionDesc	 8
+#define PTP_di_FunctionalMode		 8
+#define PTP_di_OperationsSupported	10
+
+static inline void
+ptp_unpack_DI (PTPParams *params, unsigned char* data, PTPDeviceInfo *di, unsigned int datalen)
+{
+	uint8_t len;
+	unsigned int totallen;
+
+	if (!data) return;
+	if (datalen < 12) return;
+	di->StandardVersion = dtoh16a(&data[PTP_di_StandardVersion]);
+	di->VendorExtensionID =
+		dtoh32a(&data[PTP_di_VendorExtensionID]);
+	di->VendorExtensionVersion =
+		dtoh16a(&data[PTP_di_VendorExtensionVersion]);
+	di->VendorExtensionDesc = 
+		ptp_unpack_string(params, data,
+		PTP_di_VendorExtensionDesc, &len); 
+	totallen=len*2+1;
+	di->FunctionalMode = 
+		dtoh16a(&data[PTP_di_FunctionalMode+totallen]);
+	di->OperationsSupported_len = ptp_unpack_uint16_t_array(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&di->OperationsSupported);
+	totallen=totallen+di->OperationsSupported_len*sizeof(uint16_t)+sizeof(uint32_t);
+	di->EventsSupported_len = ptp_unpack_uint16_t_array(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&di->EventsSupported);
+	totallen=totallen+di->EventsSupported_len*sizeof(uint16_t)+sizeof(uint32_t);
+	di->DevicePropertiesSupported_len =
+		ptp_unpack_uint16_t_array(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&di->DevicePropertiesSupported);
+	totallen=totallen+di->DevicePropertiesSupported_len*sizeof(uint16_t)+sizeof(uint32_t);
+	di->CaptureFormats_len = ptp_unpack_uint16_t_array(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&di->CaptureFormats);
+	totallen=totallen+di->CaptureFormats_len*sizeof(uint16_t)+sizeof(uint32_t);
+	di->ImageFormats_len = ptp_unpack_uint16_t_array(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&di->ImageFormats);
+	totallen=totallen+di->ImageFormats_len*sizeof(uint16_t)+sizeof(uint32_t);
+	di->Manufacturer = ptp_unpack_string(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&len);
+	totallen+=len*2+1;
+	di->Model = ptp_unpack_string(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&len);
+	totallen+=len*2+1;
+	di->DeviceVersion = ptp_unpack_string(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&len);
+	totallen+=len*2+1;
+	di->SerialNumber = ptp_unpack_string(params, data,
+		PTP_di_OperationsSupported+totallen,
+		&len);
+}
+
+static void
+ptp_free_DI (PTPDeviceInfo *di) {
+	if (di->SerialNumber) free (di->SerialNumber);
+	if (di->DeviceVersion) free (di->DeviceVersion);
+	if (di->Model) free (di->Model);
+	if (di->Manufacturer) free (di->Manufacturer);
+	if (di->ImageFormats) free (di->ImageFormats);
+	if (di->CaptureFormats) free (di->CaptureFormats);
+	if (di->VendorExtensionDesc) free (di->VendorExtensionDesc);
+	if (di->OperationsSupported) free (di->OperationsSupported);
+	if (di->EventsSupported) free (di->EventsSupported);
+	if (di->DevicePropertiesSupported) free (di->DevicePropertiesSupported);
+}
+
+/* EOS Device Info unpack */
+static inline void
+ptp_unpack_EOS_DI (PTPParams *params, unsigned char* data, PTPCanonEOSDeviceInfo *di, unsigned int datalen)
+{
+	int totallen = 4;
+	if (datalen < 8) return;
+
+	/* uint32_t struct len - ignore */
+	di->EventsSupported_len = ptp_unpack_uint32_t_array(params, data,
+		totallen, &di->EventsSupported);
+	if (!di->EventsSupported) return;
+	totallen += di->EventsSupported_len*sizeof(uint32_t)+4;
+	if (totallen >= datalen) return;
+
+	di->DevicePropertiesSupported_len = ptp_unpack_uint32_t_array(params, data,
+		totallen, &di->DevicePropertiesSupported);
+	if (!di->DevicePropertiesSupported) return;
+	totallen += di->DevicePropertiesSupported_len*sizeof(uint32_t)+4;
+	if (totallen >= datalen) return;
+
+	di->unk_len = ptp_unpack_uint32_t_array(params, data,
+		totallen, &di->unk);
+	if (!di->unk) return;
+	totallen += di->unk_len*sizeof(uint32_t)+4;
+	return;
+}
+	
+/* ObjectHandles array pack/unpack */
+
+#define PTP_oh				 0
+
+static inline void
+ptp_unpack_OH (PTPParams *params, unsigned char* data, PTPObjectHandles *oh, unsigned int len)
+{
+	if (len) {
+		oh->n = ptp_unpack_uint32_t_array(params, data, PTP_oh, &oh->Handler);
+	} else {
+		oh->n = 0;
+		oh->Handler = NULL;
+	} 
+}
+
+/* StoreIDs array pack/unpack */
+
+#define PTP_sids			 0
+
+static inline void
+ptp_unpack_SIDs (PTPParams *params, unsigned char* data, PTPStorageIDs *sids, unsigned int len)
+{
+	sids->n = ptp_unpack_uint32_t_array(params, data, PTP_sids,
+	&sids->Storage);
+}
+
+/* StorageInfo pack/unpack */
+
+#define PTP_si_StorageType		 0
+#define PTP_si_FilesystemType		 2
+#define PTP_si_AccessCapability		 4
+#define PTP_si_MaxCapability		 6
+#define PTP_si_FreeSpaceInBytes		14
+#define PTP_si_FreeSpaceInImages	22
+#define PTP_si_StorageDescription	26
+
+static inline void
+ptp_unpack_SI (PTPParams *params, unsigned char* data, PTPStorageInfo *si, unsigned int len)
+{
+	uint8_t storagedescriptionlen;
+
+	si->StorageType=dtoh16a(&data[PTP_si_StorageType]);
+	si->FilesystemType=dtoh16a(&data[PTP_si_FilesystemType]);
+	si->AccessCapability=dtoh16a(&data[PTP_si_AccessCapability]);
+	si->MaxCapability=dtoh64a(&data[PTP_si_MaxCapability]);
+	si->FreeSpaceInBytes=dtoh64a(&data[PTP_si_FreeSpaceInBytes]);
+	si->FreeSpaceInImages=dtoh32a(&data[PTP_si_FreeSpaceInImages]);
+	si->StorageDescription=ptp_unpack_string(params, data,
+		PTP_si_StorageDescription, &storagedescriptionlen);
+	si->VolumeLabel=ptp_unpack_string(params, data,
+		PTP_si_StorageDescription+storagedescriptionlen*2+1,
+		&storagedescriptionlen);
+}
+
+/* ObjectInfo pack/unpack */
+
+#define PTP_oi_StorageID		 0
+#define PTP_oi_ObjectFormat		 4
+#define PTP_oi_ProtectionStatus		 6
+#define PTP_oi_ObjectCompressedSize	 8
+#define PTP_oi_ThumbFormat		12
+#define PTP_oi_ThumbCompressedSize	14
+#define PTP_oi_ThumbPixWidth		18
+#define PTP_oi_ThumbPixHeight		22
+#define PTP_oi_ImagePixWidth		26
+#define PTP_oi_ImagePixHeight		30
+#define PTP_oi_ImageBitDepth		34
+#define PTP_oi_ParentObject		38
+#define PTP_oi_AssociationType		42
+#define PTP_oi_AssociationDesc		44
+#define PTP_oi_SequenceNumber		48
+#define PTP_oi_filenamelen		52
+#define PTP_oi_Filename			53
+
+/* the max length assuming zero length dates. We have need 3 */
+/* bytes for these. */
+#define PTP_oi_MaxLen PTP_oi_Filename+(PTP_MAXSTRLEN+1)*2+3
+
+static inline uint32_t
+ptp_pack_OI (PTPParams *params, PTPObjectInfo *oi, unsigned char** oidataptr)
+{
+	unsigned char* oidata;
+	uint8_t filenamelen;
+	uint8_t capturedatelen=0;
+	/* let's allocate some memory first; correct assuming zero length dates */
+	oidata=malloc(PTP_oi_MaxLen);
+	/* the caller should free it after use! */
+#if 0
+	char *capture_date="20020101T010101"; /* XXX Fake date */
+#endif
+	memset (oidata, 0, PTP_oi_MaxLen);
+	htod32a(&oidata[PTP_oi_StorageID],oi->StorageID);
+	htod16a(&oidata[PTP_oi_ObjectFormat],oi->ObjectFormat);
+	htod16a(&oidata[PTP_oi_ProtectionStatus],oi->ProtectionStatus);
+	htod32a(&oidata[PTP_oi_ObjectCompressedSize],oi->ObjectCompressedSize);
+	htod16a(&oidata[PTP_oi_ThumbFormat],oi->ThumbFormat);
+	htod32a(&oidata[PTP_oi_ThumbCompressedSize],oi->ThumbCompressedSize);
+	htod32a(&oidata[PTP_oi_ThumbPixWidth],oi->ThumbPixWidth);
+	htod32a(&oidata[PTP_oi_ThumbPixHeight],oi->ThumbPixHeight);
+	htod32a(&oidata[PTP_oi_ImagePixWidth],oi->ImagePixWidth);
+	htod32a(&oidata[PTP_oi_ImagePixHeight],oi->ImagePixHeight);
+	htod32a(&oidata[PTP_oi_ImageBitDepth],oi->ImageBitDepth);
+	htod32a(&oidata[PTP_oi_ParentObject],oi->ParentObject);
+	htod16a(&oidata[PTP_oi_AssociationType],oi->AssociationType);
+	htod32a(&oidata[PTP_oi_AssociationDesc],oi->AssociationDesc);
+	htod32a(&oidata[PTP_oi_SequenceNumber],oi->SequenceNumber);
+	
+	ptp_pack_string(params, oi->Filename, oidata, PTP_oi_filenamelen, &filenamelen);
+/*
+	filenamelen=(uint8_t)strlen(oi->Filename);
+	htod8a(&req->data[PTP_oi_filenamelen],filenamelen+1);
+	for (i=0;i<filenamelen && i< PTP_MAXSTRLEN; i++) {
+		req->data[PTP_oi_Filename+i*2]=oi->Filename[i];
+	}
+*/
+	/*
+	 *XXX Fake date.
+	 * for example Kodak sets Capture date on the basis of EXIF data.
+	 * Spec says that this field is from perspective of Initiator.
+	 */
+#if 0	/* seems now we don't need any data packed in OI dataset... for now ;)*/
+	capturedatelen=strlen(capture_date);
+	htod8a(&data[PTP_oi_Filename+(filenamelen+1)*2],
+		capturedatelen+1);
+	for (i=0;i<capturedatelen && i< PTP_MAXSTRLEN; i++) {
+		data[PTP_oi_Filename+(i+filenamelen+1)*2+1]=capture_date[i];
+	}
+	htod8a(&data[PTP_oi_Filename+(filenamelen+capturedatelen+2)*2+1],
+		capturedatelen+1);
+	for (i=0;i<capturedatelen && i< PTP_MAXSTRLEN; i++) {
+		data[PTP_oi_Filename+(i+filenamelen+capturedatelen+2)*2+2]=
+		  capture_date[i];
+	}
+#endif
+	/* XXX this function should return dataset length */
+	
+	*oidataptr=oidata;
+	return (PTP_oi_Filename+filenamelen*2+(capturedatelen+1)*3);
+}
+
+static time_t
+ptp_unpack_PTPTIME (const char *str) {
+	char ptpdate[40];
+	char tmp[5];
+	int  ptpdatelen;
+	struct tm tm;
+
+	if (!str)
+		return 0;
+	ptpdatelen = strlen(str);
+	if (ptpdatelen >= sizeof (ptpdate)) {
+		/*ptp_debug (params ,"datelen is larger then size of buffer", ptpdatelen, (int)sizeof(ptpdate));*/
+		return 0;
+	}
+	strcpy (ptpdate, str);
+	if (ptpdatelen<15) {
+		/*ptp_debug (params ,"datelen is less than 15 (%d)", ptpdatelen);*/
+		return 0;
+	}
+
+	memset(&tm,0,sizeof(tm));
+	strncpy (tmp, ptpdate, 4);
+	tmp[4] = 0;
+	tm.tm_year=atoi (tmp) - 1900;
+	strncpy (tmp, ptpdate + 4, 2);
+	tmp[2] = 0;
+	tm.tm_mon = atoi (tmp) - 1;
+	strncpy (tmp, ptpdate + 6, 2);
+	tmp[2] = 0;
+	tm.tm_mday = atoi (tmp);
+	strncpy (tmp, ptpdate + 9, 2);
+	tmp[2] = 0;
+	tm.tm_hour = atoi (tmp);
+	strncpy (tmp, ptpdate + 11, 2);
+	tmp[2] = 0;
+	tm.tm_min = atoi (tmp);
+	strncpy (tmp, ptpdate + 13, 2);
+	tmp[2] = 0;
+	tm.tm_sec = atoi (tmp);
+
+	tm.tm_isdst = -1;
+
+	return mktime (&tm);
+}
+
+static inline void
+ptp_unpack_OI (PTPParams *params, unsigned char* data, PTPObjectInfo *oi, unsigned int len)
+{
+	uint8_t filenamelen;
+	uint8_t capturedatelen;
+	char *capture_date;
+
+	oi->StorageID=dtoh32a(&data[PTP_oi_StorageID]);
+	oi->ObjectFormat=dtoh16a(&data[PTP_oi_ObjectFormat]);
+	oi->ProtectionStatus=dtoh16a(&data[PTP_oi_ProtectionStatus]);
+	oi->ObjectCompressedSize=dtoh32a(&data[PTP_oi_ObjectCompressedSize]);
+	oi->ThumbFormat=dtoh16a(&data[PTP_oi_ThumbFormat]);
+	oi->ThumbCompressedSize=dtoh32a(&data[PTP_oi_ThumbCompressedSize]);
+	oi->ThumbPixWidth=dtoh32a(&data[PTP_oi_ThumbPixWidth]);
+	oi->ThumbPixHeight=dtoh32a(&data[PTP_oi_ThumbPixHeight]);
+	oi->ImagePixWidth=dtoh32a(&data[PTP_oi_ImagePixWidth]);
+	oi->ImagePixHeight=dtoh32a(&data[PTP_oi_ImagePixHeight]);
+	oi->ImageBitDepth=dtoh32a(&data[PTP_oi_ImageBitDepth]);
+	oi->ParentObject=dtoh32a(&data[PTP_oi_ParentObject]);
+	oi->AssociationType=dtoh16a(&data[PTP_oi_AssociationType]);
+	oi->AssociationDesc=dtoh32a(&data[PTP_oi_AssociationDesc]);
+	oi->SequenceNumber=dtoh32a(&data[PTP_oi_SequenceNumber]);
+	oi->Filename= ptp_unpack_string(params, data, PTP_oi_filenamelen, &filenamelen);
+
+	capture_date = ptp_unpack_string(params, data,
+		PTP_oi_filenamelen+filenamelen*2+1, &capturedatelen);
+	/* subset of ISO 8601, without '.s' tenths of second and 
+	 * time zone
+	 */
+	oi->CaptureDate = ptp_unpack_PTPTIME(capture_date);
+	free(capture_date);
+
+	/* now the modification date ... */
+	capture_date = ptp_unpack_string(params, data,
+		PTP_oi_filenamelen+filenamelen*2
+		+capturedatelen*2+2,&capturedatelen);
+	oi->ModificationDate = ptp_unpack_PTPTIME(capture_date);
+	free(capture_date);
+}
+
+/* Custom Type Value Assignement (without Length) macro frequently used below */
+#define CTVAL(target,func) {			\
+	if (total - *offset < sizeof(target))	\
+		return 0;			\
+	target = func(&data[*offset]);		\
+	*offset += sizeof(target);		\
+}
+
+#define RARR(val,member,func)	{			\
+	int n,j;					\
+	if (total - *offset < sizeof(uint32_t))		\
+		return 0;				\
+	n = dtoh32a (&data[*offset]);			\
+	*offset += sizeof(uint32_t);			\
+							\
+	val->a.count = n;				\
+	val->a.v = malloc(sizeof(val->a.v[0])*n);	\
+	if (!val->a.v) return 0;			\
+	for (j=0;j<n;j++)				\
+		CTVAL(val->a.v[j].member, func);	\
+}
+
+static inline int
+ptp_unpack_DPV (
+	PTPParams *params, unsigned char* data, int *offset, int total,
+	PTPPropertyValue* value, uint16_t datatype
+) {
+	switch (datatype) {
+	case PTP_DTC_INT8:
+		CTVAL(value->i8,dtoh8a);
+		break;
+	case PTP_DTC_UINT8:
+		CTVAL(value->u8,dtoh8a);
+		break;
+	case PTP_DTC_INT16:
+		CTVAL(value->i16,dtoh16a);
+		break;
+	case PTP_DTC_UINT16:
+		CTVAL(value->u16,dtoh16a);
+		break;
+	case PTP_DTC_INT32:
+		CTVAL(value->i32,dtoh32a);
+		break;
+	case PTP_DTC_UINT32:
+		CTVAL(value->u32,dtoh32a);
+		break;
+	case PTP_DTC_INT64:
+		CTVAL(value->i64,dtoh64a);
+		break;
+	case PTP_DTC_UINT64:
+		CTVAL(value->u64,dtoh64a);
+		break;
+
+	case PTP_DTC_UINT128:
+		*offset += 16;
+		/*fprintf(stderr,"unhandled unpack of uint128n");*/
+		break;
+	case PTP_DTC_INT128:
+		*offset += 16;
+		/*fprintf(stderr,"unhandled unpack of int128n");*/
+		break;
+
+
+
+	case PTP_DTC_AINT8:
+		RARR(value,i8,dtoh8a);
+		break;
+	case PTP_DTC_AUINT8:
+		RARR(value,u8,dtoh8a);
+		break;
+	case PTP_DTC_AUINT16:
+		RARR(value,u16,dtoh16a);
+		break;
+	case PTP_DTC_AINT16:
+		RARR(value,i16,dtoh16a);
+		break;
+	case PTP_DTC_AUINT32:
+		RARR(value,u32,dtoh32a);
+		break;
+	case PTP_DTC_AINT32:
+		RARR(value,i32,dtoh32a);
+		break;
+	case PTP_DTC_AUINT64:
+		RARR(value,u64,dtoh64a);
+		break;
+	case PTP_DTC_AINT64:
+		RARR(value,i64,dtoh64a);
+		break;
+	/* XXX: other int types are unimplemented */
+	/* XXX: other int arrays are unimplemented also */
+	case PTP_DTC_STR: {
+		uint8_t len;
+		/* XXX: max size */
+		value->str = ptp_unpack_string(params,data,*offset,&len);
+		*offset += len*2+1;
+		if (!value->str)
+			return 1;
+		break;
+	}
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+/* Device Property pack/unpack */
+
+#define PTP_dpd_DevicePropertyCode	0
+#define PTP_dpd_DataType		2
+#define PTP_dpd_GetSet			4
+#define PTP_dpd_FactoryDefaultValue	5
+
+static inline int
+ptp_unpack_DPD (PTPParams *params, unsigned char* data, PTPDevicePropDesc *dpd, unsigned int dpdlen)
+{
+	int offset=0, ret;
+
+	memset (dpd, 0, sizeof(*dpd));
+	dpd->DevicePropertyCode=dtoh16a(&data[PTP_dpd_DevicePropertyCode]);
+	dpd->DataType=dtoh16a(&data[PTP_dpd_DataType]);
+	dpd->GetSet=dtoh8a(&data[PTP_dpd_GetSet]);
+	dpd->FormFlag=PTP_DPFF_None;
+
+	offset = PTP_dpd_FactoryDefaultValue;
+	ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->FactoryDefaultValue, dpd->DataType);
+	if (!ret) goto outofmemory;
+	if ((dpd->DataType == PTP_DTC_STR) && (offset == dpdlen))
+		return 1;
+	ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->CurrentValue, dpd->DataType);
+	if (!ret) goto outofmemory;
+
+	/* if offset==0 then Data Type format is not supported by this
+	   code or the Data Type is a string (with two empty strings as
+	   values). In both cases Form Flag should be set to 0x00 and FORM is
+	   not present. */
+
+	if (offset==PTP_dpd_FactoryDefaultValue)
+		return 1;
+
+	dpd->FormFlag=dtoh8a(&data[offset]);
+	offset+=sizeof(uint8_t);
+
+	switch (dpd->FormFlag) {
+	case PTP_DPFF_Range:
+		ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->FORM.Range.MinimumValue, dpd->DataType);
+		if (!ret) goto outofmemory;
+		ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->FORM.Range.MaximumValue, dpd->DataType);
+		if (!ret) goto outofmemory;
+		ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->FORM.Range.StepSize, dpd->DataType);
+		if (!ret) goto outofmemory;
+		break;
+	case PTP_DPFF_Enumeration: {
+		int i;
+#define N	dpd->FORM.Enum.NumberOfValues
+		N = dtoh16a(&data[offset]);
+		offset+=sizeof(uint16_t);
+		dpd->FORM.Enum.SupportedValue = malloc(N*sizeof(dpd->FORM.Enum.SupportedValue[0]));
+		if (!dpd->FORM.Enum.SupportedValue)
+			goto outofmemory;
+
+		memset (dpd->FORM.Enum.SupportedValue,0 , N*sizeof(dpd->FORM.Enum.SupportedValue[0]));
+		for (i=0;i<N;i++) {
+			ret = ptp_unpack_DPV (params, data, &offset, dpdlen, &dpd->FORM.Enum.SupportedValue[i], dpd->DataType);
+
+			/* Slightly different handling here. The HP PhotoSmart 120
+			 * specifies an enumeration with N in wrong endian
+			 * 00 01 instead of 01 00, so we count the enum just until the
+			 * the end of the packet.
+			 */
+			if (!ret) {
+				if (!i)
+					goto outofmemory;
+				dpd->FORM.Enum.NumberOfValues = i;
+				break;
+			}
+		}
+		}
+	}
+#undef N
+	return 1;
+outofmemory:
+	ptp_free_devicepropdesc(dpd);
+	return 0;
+}
+
+/* (MTP) Object Property pack/unpack */
+#define PTP_opd_ObjectPropertyCode	0
+#define PTP_opd_DataType		2
+#define PTP_opd_GetSet			4
+#define PTP_opd_FactoryDefaultValue	5
+
+static inline int
+ptp_unpack_OPD (PTPParams *params, unsigned char* data, PTPObjectPropDesc *opd, unsigned int opdlen)
+{
+	int offset=0, ret;
+
+	memset (opd, 0, sizeof(*opd));
+	opd->ObjectPropertyCode=dtoh16a(&data[PTP_opd_ObjectPropertyCode]);
+	opd->DataType=dtoh16a(&data[PTP_opd_DataType]);
+	opd->GetSet=dtoh8a(&data[PTP_opd_GetSet]);
+
+	offset = PTP_opd_FactoryDefaultValue;
+	ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FactoryDefaultValue, opd->DataType);
+	if (!ret) goto outofmemory;
+
+	opd->GroupCode=dtoh32a(&data[offset]);
+	offset+=sizeof(uint32_t);
+
+	opd->FormFlag=dtoh8a(&data[offset]);
+	offset+=sizeof(uint8_t);
+
+	switch (opd->FormFlag) {
+	case PTP_OPFF_Range:
+		ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.MinimumValue, opd->DataType);
+		if (!ret) goto outofmemory;
+		ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.MaximumValue, opd->DataType);
+		if (!ret) goto outofmemory;
+		ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.StepSize, opd->DataType);
+		if (!ret) goto outofmemory;
+		break;
+	case PTP_OPFF_Enumeration: {
+		int i;
+#define N	opd->FORM.Enum.NumberOfValues
+		N = dtoh16a(&data[offset]);
+		offset+=sizeof(uint16_t);
+		opd->FORM.Enum.SupportedValue = malloc(N*sizeof(opd->FORM.Enum.SupportedValue[0]));
+		if (!opd->FORM.Enum.SupportedValue)
+			goto outofmemory;
+
+		memset (opd->FORM.Enum.SupportedValue,0 , N*sizeof(opd->FORM.Enum.SupportedValue[0]));
+		for (i=0;i<N;i++) {
+			ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Enum.SupportedValue[i], opd->DataType);
+
+			/* Slightly different handling here. The HP PhotoSmart 120
+			 * specifies an enumeration with N in wrong endian
+			 * 00 01 instead of 01 00, so we count the enum just until the
+			 * the end of the packet.
+			 */
+			if (!ret) {
+				if (!i)
+					goto outofmemory;
+				opd->FORM.Enum.NumberOfValues = i;
+				break;
+			}
+		}
+#undef N
+		}
+	}
+	return 1;
+outofmemory:
+	ptp_free_objectpropdesc(opd);
+	return 0;
+}
+
+
+static inline uint32_t
+ptp_pack_DPV (PTPParams *params, PTPPropertyValue* value, unsigned char** dpvptr, uint16_t datatype)
+{
+	unsigned char* dpv=NULL;
+	uint32_t size=0;
+	int	i;
+
+	switch (datatype) {
+	case PTP_DTC_INT8:
+		size=sizeof(int8_t);
+		dpv=malloc(size);
+		htod8a(dpv,value->i8);
+		break;
+	case PTP_DTC_UINT8:
+		size=sizeof(uint8_t);
+		dpv=malloc(size);
+		htod8a(dpv,value->u8);
+		break;
+	case PTP_DTC_INT16:
+		size=sizeof(int16_t);
+		dpv=malloc(size);
+		htod16a(dpv,value->i16);
+		break;
+	case PTP_DTC_UINT16:
+		size=sizeof(uint16_t);
+		dpv=malloc(size);
+		htod16a(dpv,value->u16);
+		break;
+	case PTP_DTC_INT32:
+		size=sizeof(int32_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->i32);
+		break;
+	case PTP_DTC_UINT32:
+		size=sizeof(uint32_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->u32);
+		break;
+	case PTP_DTC_INT64:
+		size=sizeof(int64_t);
+		dpv=malloc(size);
+		htod64a(dpv,value->i64);
+		break;
+	case PTP_DTC_UINT64:
+		size=sizeof(uint64_t);
+		dpv=malloc(size);
+		htod64a(dpv,value->u64);
+		break;
+	case PTP_DTC_AUINT8:
+		size=sizeof(uint32_t)+value->a.count*sizeof(uint8_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod8a(&dpv[sizeof(uint32_t)+i*sizeof(uint8_t)],value->a.v[i].u8);
+		break;
+	case PTP_DTC_AINT8:
+		size=sizeof(uint32_t)+value->a.count*sizeof(int8_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod8a(&dpv[sizeof(uint32_t)+i*sizeof(int8_t)],value->a.v[i].i8);
+		break;
+	case PTP_DTC_AUINT16:
+		size=sizeof(uint32_t)+value->a.count*sizeof(uint16_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod16a(&dpv[sizeof(uint32_t)+i*sizeof(uint16_t)],value->a.v[i].u16);
+		break;
+	case PTP_DTC_AINT16:
+		size=sizeof(uint32_t)+value->a.count*sizeof(int16_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod16a(&dpv[sizeof(uint32_t)+i*sizeof(int16_t)],value->a.v[i].i16);
+		break;
+	case PTP_DTC_AUINT32:
+		size=sizeof(uint32_t)+value->a.count*sizeof(uint32_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod32a(&dpv[sizeof(uint32_t)+i*sizeof(uint32_t)],value->a.v[i].u32);
+		break;
+	case PTP_DTC_AINT32:
+		size=sizeof(uint32_t)+value->a.count*sizeof(int32_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod32a(&dpv[sizeof(uint32_t)+i*sizeof(int32_t)],value->a.v[i].i32);
+		break;
+	case PTP_DTC_AUINT64:
+		size=sizeof(uint32_t)+value->a.count*sizeof(uint64_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod64a(&dpv[sizeof(uint32_t)+i*sizeof(uint64_t)],value->a.v[i].u64);
+		break;
+	case PTP_DTC_AINT64:
+		size=sizeof(uint32_t)+value->a.count*sizeof(int64_t);
+		dpv=malloc(size);
+		htod32a(dpv,value->a.count);
+		for (i=0;i<value->a.count;i++)
+			htod64a(&dpv[sizeof(uint32_t)+i*sizeof(int64_t)],value->a.v[i].i64);
+		break;
+	/* XXX: other int types are unimplemented */
+	case PTP_DTC_STR: {
+		dpv=ptp_get_packed_stringcopy(params, value->str, &size);
+		break;
+	}
+	}
+	*dpvptr=dpv;
+	return size;
+}
+
+#define MAX_MTP_PROPS 127
+static inline uint32_t
+ptp_pack_OPL (PTPParams *params, MTPProperties *props, int nrofprops, unsigned char** opldataptr)
+{
+	unsigned char* opldata;
+	MTPProperties *propitr;
+	unsigned char *packedprops[MAX_MTP_PROPS];
+	uint32_t packedpropslens[MAX_MTP_PROPS];
+	uint32_t packedobjecthandles[MAX_MTP_PROPS];
+	uint16_t packedpropsids[MAX_MTP_PROPS];
+	uint16_t packedpropstypes[MAX_MTP_PROPS];
+	uint32_t totalsize = 0;
+	uint32_t bufp = 0;
+	uint32_t noitems = 0;
+	uint32_t i;
+
+	totalsize = sizeof(uint32_t); /* 4 bytes to store the number of elements */
+	propitr = props;
+	while (nrofprops-- && noitems < MAX_MTP_PROPS) {
+		/* Object Handle */
+		packedobjecthandles[noitems]=propitr->ObjectHandle;
+		totalsize += sizeof(uint32_t); /* Object ID */
+		/* Metadata type */
+		packedpropsids[noitems]=propitr->property;
+		totalsize += sizeof(uint16_t);
+		/* Data type */
+		packedpropstypes[noitems]= propitr->datatype;
+		totalsize += sizeof(uint16_t);
+		/* Add each property to be sent. */
+	        packedpropslens[noitems] = ptp_pack_DPV (params, &propitr->propval, &packedprops[noitems], propitr->datatype);
+		totalsize += packedpropslens[noitems];
+		noitems ++;
+		propitr ++;
+	}
+
+	/* Allocate memory for the packed property list */
+	opldata = malloc(totalsize);
+
+	htod32a(&opldata[bufp],noitems);
+	bufp += 4;
+
+	/* Copy into a nice packed list */
+	for (i = 0; i < noitems; i++) {
+		/* Object ID */
+		htod32a(&opldata[bufp],packedobjecthandles[i]);
+		bufp += sizeof(uint32_t);
+		htod16a(&opldata[bufp],packedpropsids[i]);
+		bufp += sizeof(uint16_t);
+		htod16a(&opldata[bufp],packedpropstypes[i]);
+		bufp += sizeof(uint16_t);
+		/* The copy the actual property */
+		memcpy(&opldata[bufp], packedprops[i], packedpropslens[i]);
+		bufp += packedpropslens[i];
+		free(packedprops[i]);
+	}
+	*opldataptr = opldata;
+	return totalsize;
+}
+
+static int
+_compare_func(const void* x, const void *y) {
+	const MTPProperties *px = x;
+	const MTPProperties *py = y;
+
+	return px->ObjectHandle - py->ObjectHandle;
+}
+
+static inline int
+ptp_unpack_OPL (PTPParams *params, unsigned char* data, MTPProperties **pprops, unsigned int len)
+{ 
+	uint32_t prop_count = dtoh32a(data);
+	MTPProperties *props = NULL;
+	int offset = 0, i;
+
+	if (prop_count == 0) {
+		*pprops = NULL;
+		return 0;
+	}
+	ptp_debug (params ,"Unpacking MTP OPL, size %d (prop_count %d)", len, prop_count);
+	data += sizeof(uint32_t);
+	len -= sizeof(uint32_t);
+	props = malloc(prop_count * sizeof(MTPProperties));
+	if (!props) return 0;
+	for (i = 0; i < prop_count; i++) {
+		if (len <= 0) {
+			ptp_debug (params ,"short MTP Object Property List at property %d (of %d)", i, prop_count);
+			ptp_debug (params ,"device probably needs DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL", i);
+			ptp_debug (params ,"or even DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST", i);
+			qsort (props, i, sizeof(MTPProperties),_compare_func);
+			*pprops = props;
+			return i;
+		}
+		props[i].ObjectHandle = dtoh32a(data);
+		data += sizeof(uint32_t);
+		len -= sizeof(uint32_t);
+
+		props[i].property = dtoh16a(data);
+		data += sizeof(uint16_t);
+		len -= sizeof(uint16_t);
+
+		props[i].datatype = dtoh16a(data);
+		data += sizeof(uint16_t);
+		len -= sizeof(uint16_t);
+
+		offset = 0;
+		ptp_unpack_DPV(params, data, &offset, len, &props[i].propval, props[i].datatype);
+		data += offset;
+		len -= offset;
+	}
+	qsort (props, prop_count, sizeof(MTPProperties),_compare_func);
+	*pprops = props;
+	return prop_count;
+}
+
+/*
+    PTP USB Event container unpack
+    Copyright (c) 2003 Nikolai Kopanygin
+*/
+
+#define PTP_ec_Length		0
+#define PTP_ec_Type		4
+#define PTP_ec_Code		6
+#define PTP_ec_TransId		8
+#define PTP_ec_Param1		12
+#define PTP_ec_Param2		16
+#define PTP_ec_Param3		20
+
+static inline void
+ptp_unpack_EC (PTPParams *params, unsigned char* data, PTPContainer *ec, unsigned int len)
+{
+	int	length;
+	int	type;
+
+	if (data==NULL)
+		return;
+	memset(ec,0,sizeof(*ec));
+	length=dtoh32a(&data[PTP_ec_Length]);
+	type = dtoh16a(&data[PTP_ec_Type]);
+
+	ec->Code=dtoh16a(&data[PTP_ec_Code]);
+	ec->Transaction_ID=dtoh32a(&data[PTP_ec_TransId]);
+
+	if (type!=PTP_USB_CONTAINER_EVENT) {
+		ptp_debug (params, "Unknown canon event type %d (code=%x,tid=%x), please report!",type,ec->Code,ec->Transaction_ID);
+		return;
+	}
+	if (length>=(PTP_ec_Param1+4)) {
+		ec->Param1=dtoh32a(&data[PTP_ec_Param1]);
+		ec->Nparam=1;
+	}
+	if (length>=(PTP_ec_Param2+4)) {
+		ec->Param2=dtoh32a(&data[PTP_ec_Param2]);
+		ec->Nparam=2;
+	}
+	if (length>=(PTP_ec_Param3+4)) {
+		ec->Param3=dtoh32a(&data[PTP_ec_Param3]);
+		ec->Nparam=3;
+	}
+}
+
+/*
+    PTP Canon Folder Entry unpack
+    Copyright (c) 2003 Nikolai Kopanygin
+*/
+#define PTP_cfe_ObjectHandle		0
+#define PTP_cfe_ObjectFormatCode	4
+#define PTP_cfe_Flags			6
+#define PTP_cfe_ObjectSize		7
+#define PTP_cfe_Time			11
+#define PTP_cfe_Filename		15
+
+static inline void
+ptp_unpack_Canon_FE (PTPParams *params, unsigned char* data, PTPCANONFolderEntry *fe)
+{
+	int i;
+	if (data==NULL)
+		return;
+	fe->ObjectHandle=dtoh32a(&data[PTP_cfe_ObjectHandle]);
+	fe->ObjectFormatCode=dtoh16a(&data[PTP_cfe_ObjectFormatCode]);
+	fe->Flags=dtoh8a(&data[PTP_cfe_Flags]);
+	fe->ObjectSize=dtoh32a((unsigned char*)&data[PTP_cfe_ObjectSize]);
+	fe->Time=(time_t)dtoh32a(&data[PTP_cfe_Time]);
+	for (i=0; i<PTP_CANON_FilenameBufferLen; i++)
+		fe->Filename[i]=(char)dtoh8a(&data[PTP_cfe_Filename+i]);
+}
+
+static inline uint16_t
+ptp_unpack_EOS_ImageFormat (PTPParams* params, unsigned char** data )
+{
+	/*
+	  EOS ImageFormat entries (of at least the 5DMII and the 400D ) look like this:
+		uint32: number of entries / generated files (1 or 2)
+		uint32: size of this entry in bytes (most likely allways 0x10)
+		uint32: image type (1 == JPG, 6 == RAW)
+		uint32: image size (0 == Large, 1 == Medium, 2 == Small)
+		uint32: image compression (2 == Standard/JPG, 3 == Fine/JPG, 4 == Lossles/RAW)
+	  If number of entries is 2 the last uint32 repeat.
+
+	  example:
+		0: 0x       1
+		1: 0x      10
+		2: 0x       6
+		3: 0x       1
+		4: 0x       4
+
+	  The idea is to simply 'condense' these values to just one uint16 to be able to conveniontly
+	  use the available enumeration facilities (look-up table). The image size and compression
+	  values fully describe the image format. Hence we generate a uint16 with the four nibles set
+	  as follows: entry 1 size | entry 1 compression | entry 2 size | entry 2 compression.
+	  The above example would result in the value 0x1400.
+	  */
+
+	const unsigned char* d = *data;
+	uint32_t n = dtoh32a( d );
+	uint32_t l, s1, c1, s2 = 0, c2 = 0;
+
+	if (n != 1 && n !=2) {
+		ptp_debug (params, "parsing EOS ImageFormat property failed (n != 1 && n != 2: %d)", n);
+		return 0;
+	}
+
+	l = dtoh32a( d+=4 );
+	if (l != 0x10) {
+		ptp_debug (params, "parsing EOS ImageFormat property failed (l != 0x10: 0x%x)", l);
+		return 0;
+	}
+
+	d+=4; /* skip type */
+	s1 = dtoh32a( d+=4 );
+	c1 = dtoh32a( d+=4 );
+
+	if (n == 2) {
+		l = dtoh32a( d+=4 );
+		if (l != 0x10) {
+			ptp_debug (params, "parsing EOS ImageFormat property failed (l != 0x10: 0x%x)", l);
+			return 0;
+		}
+		d+=4; /* skip type */
+		s2 = dtoh32a( d+=4 );
+		c2 = dtoh32a( d+=4 );
+	}
+
+	*data = (unsigned char*) d+4;
+
+	return ((s1 & 0xF) << 12) | ((c1 & 0xF) << 8) | ((s2 & 0xF) << 4) | ((c2 & 0xF) << 0);
+}
+
+static inline uint32_t
+ptp_pack_EOS_ImageFormat (PTPParams* params, unsigned char* data, uint16_t value)
+{
+	uint32_t n = (value & 0xFF) ? 2 : 1;
+	uint32_t s = 4 + 0x10 * n;
+
+	if( !data )
+		return s;
+
+	htod32a(data+=0, n);
+	htod32a(data+=4, 0x10);
+	htod32a(data+=4, ((value >> 8) & 0xF) == 4 ? 6 : 1);
+	htod32a(data+=4, (value >> 12) & 0xF);
+	htod32a(data+=4, (value >> 8) & 0xF);
+
+	if (n==2) {
+		htod32a(data+=4, 0x10);
+		htod32a(data+=4, ((value >> 0) & 0xF) == 4 ? 6 : 1);
+		htod32a(data+=4, (value >> 4) & 0xF);
+		htod32a(data+=4, (value >> 0) & 0xF);
+	}
+
+	return s;
+}
+
+/*
+    PTP EOS Changes Entry unpack
+*/
+#define PTP_ece_Size		0
+#define PTP_ece_Type		4
+
+#define PTP_ece_Prop_Subtype	8	/* only for properties */
+#define PTP_ece_Prop_Val_Data	0xc	/* only for properties */
+#define PTP_ece_Prop_Desc_Type	0xc	/* only for property descs */
+#define PTP_ece_Prop_Desc_Count	0x10	/* only for property descs */
+#define PTP_ece_Prop_Desc_Data	0x14	/* only for property descs */
+
+/* for PTP_EC_CANON_EOS_RequestObjectTransfer */
+#define PTP_ece_OI_ObjectID	8
+#define PTP_ece_OI_OFC		0x0c
+#define PTP_ece_OI_Size		0x14
+#define PTP_ece_OI_Name		0x1c
+
+/* for PTP_EC_CANON_EOS_ObjectAddedEx */
+#define PTP_ece_OA_ObjectID	8
+#define PTP_ece_OA_StorageID	0x0c
+#define PTP_ece_OA_OFC		0x10
+#define PTP_ece_OA_Size		0x1c
+#define PTP_ece_OA_Parent	0x20
+#define PTP_ece_OA_Name		0x28
+
+static inline int
+ptp_unpack_CANON_changes (PTPParams *params, unsigned char* data, int datasize, PTPCanon_changes_entry **ce)
+{
+	int	i = 0, entries = 0;
+	unsigned char	*curdata = data;
+
+	if (data==NULL)
+		return 0;
+	while (curdata - data < datasize) {
+		uint32_t	size = dtoh32a(&curdata[PTP_ece_Size]);
+		uint32_t	type = dtoh32a(&curdata[PTP_ece_Type]);
+
+		curdata += size;
+		if ((size == 8) && (type == 0))
+			break;
+		entries++;
+	}
+	*ce = malloc (sizeof(PTPCanon_changes_entry)*(entries+1));
+	if (!*ce) return 0;
+
+	curdata = data;
+	while (curdata - data < datasize) {
+		uint32_t	size = dtoh32a(&curdata[PTP_ece_Size]);
+		uint32_t	type = dtoh32a(&curdata[PTP_ece_Type]);
+
+		(*ce)[i].type = PTP_CANON_EOS_CHANGES_TYPE_UNKNOWN;
+		switch (type) {
+		case  PTP_EC_CANON_EOS_ObjectAddedEx:
+			(*ce)[i].type = PTP_CANON_EOS_CHANGES_TYPE_OBJECTINFO;
+			(*ce)[i].u.object.oid    		= dtoh32a(&curdata[PTP_ece_OA_ObjectID]);
+			(*ce)[i].u.object.oi.StorageID 		= dtoh32a(&curdata[PTP_ece_OA_StorageID]);
+			(*ce)[i].u.object.oi.ParentObject	= dtoh32a(&curdata[PTP_ece_OA_Parent]);
+			(*ce)[i].u.object.oi.ObjectFormat 	= dtoh16a(&curdata[PTP_ece_OA_OFC]);
+			(*ce)[i].u.object.oi.ObjectCompressedSize= dtoh32a(&curdata[PTP_ece_OA_Size]);
+			(*ce)[i].u.object.oi.Filename 		= strdup(((char*)&curdata[PTP_ece_OA_Name]));
+			ptp_debug (params, "event %d: objectinfo added oid %08lx, parent %08lx, ofc %04x, size %d, filename %s", i, (*ce)[i].u.object.oid, (*ce)[i].u.object.oi.ParentObject, (*ce)[i].u.object.oi.ObjectFormat, (*ce)[i].u.object.oi.ObjectCompressedSize, (*ce)[i].u.object.oi.Filename);
+			break;
+		case  PTP_EC_CANON_EOS_RequestObjectTransfer:
+			(*ce)[i].type = PTP_CANON_EOS_CHANGES_TYPE_OBJECTTRANSFER;
+			(*ce)[i].u.object.oid    		= dtoh32a(&curdata[PTP_ece_OI_ObjectID]);
+			(*ce)[i].u.object.oi.StorageID 		= 0; /* use as marker */
+			(*ce)[i].u.object.oi.ObjectFormat 	= dtoh16a(&curdata[PTP_ece_OI_OFC]);
+			(*ce)[i].u.object.oi.ParentObject	= 0; /* check, but use as marker */
+			(*ce)[i].u.object.oi.ObjectCompressedSize = dtoh32a(&curdata[PTP_ece_OI_Size]);
+			(*ce)[i].u.object.oi.Filename 		= strdup(((char*)&curdata[PTP_ece_OI_Name]));
+
+			ptp_debug (params, "event %d: request object transfer oid %08lx, ofc %04x, size %d, filename %s", i, (*ce)[i].u.object.oid, (*ce)[i].u.object.oi.ObjectFormat, (*ce)[i].u.object.oi.ObjectCompressedSize, (*ce)[i].u.object.oi.Filename);
+			break;
+		case  PTP_EC_CANON_EOS_AvailListChanged: {	/* property desc */
+			uint32_t	proptype = dtoh32a(&curdata[PTP_ece_Prop_Subtype]);
+			uint32_t	propxtype = dtoh32a(&curdata[PTP_ece_Prop_Desc_Type]);
+			uint32_t	propxcnt = dtoh32a(&curdata[PTP_ece_Prop_Desc_Count]);
+			unsigned char	*data = &curdata[PTP_ece_Prop_Desc_Data];
+			int		j;
+			PTPDevicePropDesc	*dpd;
+
+			ptp_debug (params, "event %d: EOS prop %04x desc record, datasize %d, propxtype %d", i, proptype, size-PTP_ece_Prop_Desc_Data, propxtype);
+			for (j=0;j<params->nrofcanon_props;j++)
+				if (params->canon_props[j].proptype == proptype)
+					break;
+			if (j==params->nrofcanon_props) {
+				ptp_debug (params, "event %d: propdesc %x, default value not found.", i, proptype);
+				break;
+			}
+			dpd = &params->canon_props[j].dpd;
+			/* 1 - uint16 ? 
+			 * 3 - uint16
+			 * 7 - string?
+			 */
+			if (propxtype != 3) {
+				ptp_debug (params, "event %d: propxtype is %x for %04x, unhandled.", i, propxtype, proptype);
+				for (j=0;j<size-PTP_ece_Prop_Desc_Data;j++)
+					ptp_debug (params, "    %d: %02x", j, data[j]);
+				break;
+			}
+			if (! propxcnt)
+				break;
+
+			ptp_debug (params, "event %d: propxtype is %x, prop is 0x%04x, data type is 0x%04x, propxcnt is %d.",
+				   i, propxtype, proptype, dpd->DataType, propxcnt);
+			dpd->FormFlag = PTP_DPFF_Enumeration;
+			dpd->FORM.Enum.NumberOfValues = propxcnt;
+			dpd->FORM.Enum.SupportedValue = malloc (sizeof (PTPPropertyValue)*propxcnt);
+
+			switch (proptype) {
+			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 */
+				for (j=0;j<propxcnt;j++) {
+					dpd->FORM.Enum.SupportedValue[j].u16 =
+							dtoh16( ptp_unpack_EOS_ImageFormat( params, &data ) );
+					ptp_debug (params, "event %d: suppval[%d] of %x is 0x%x.", i, j, proptype, dpd->FORM.Enum.SupportedValue[j].u16);
+				}
+				break;
+			default:
+				/* 'normal' enumerated types */
+				switch (dpd->DataType) {
+#define XX( TYPE, CONV )\
+					for (j=0;j<propxcnt;j++) { \
+						dpd->FORM.Enum.SupportedValue[j].TYPE = CONV(data); \
+						ptp_debug (params, "event %d: suppval[%d] of %x is 0x%x.", i, j, proptype, CONV(data)); \
+						data += 4; /* might only be for propxtype 3 */ \
+					} \
+					break;
+
+				case PTP_DTC_INT16:	XX( i16, dtoh16a );
+				case PTP_DTC_UINT32:	XX( u32, dtoh32a );
+				case PTP_DTC_UINT16:	XX( u16, dtoh16a );
+				case PTP_DTC_UINT8:	XX( u8,  dtoh8a );
+#undef XX
+				default:
+					ptp_debug (params ,"event %d: data type 0x%04x of %x unhandled, raw values:", i, dpd->DataType, proptype, dtoh32a(data));
+					for (j=0;j<(size-PTP_ece_Prop_Desc_Data)/4;j++, data+=4) /* 4 is good for propxtype 3 */
+						ptp_debug (params, "    %3d: 0x%8x", j, dtoh32a(data));
+					break;
+				}
+			}
+			break;
+		}
+		case PTP_EC_CANON_EOS_PropValueChanged:
+			if (size >= 0xc) {	/* property info */
+				int j;
+				uint32_t	proptype = dtoh32a(&curdata[PTP_ece_Prop_Subtype]);
+				unsigned char	*data = &curdata[PTP_ece_Prop_Val_Data];
+				PTPDevicePropDesc	*dpd;
+
+				ptp_debug (params, "event %d: EOS prop %04x info record, datasize is %d", i, proptype, size-PTP_ece_Prop_Val_Data);
+				for (j=0;j<params->nrofcanon_props;j++)
+					if (params->canon_props[j].proptype == proptype)
+						break;
+				if (j<params->nrofcanon_props) {
+					if (	(params->canon_props[j].size != size) ||
+						(memcmp(params->canon_props[j].data,data,size-PTP_ece_Prop_Val_Data))) {
+						params->canon_props[j].data = realloc(params->canon_props[j].data,size-PTP_ece_Prop_Val_Data);
+						memcpy (params->canon_props[j].data,data,size-PTP_ece_Prop_Val_Data);
+					}
+				} else {
+					if (j)
+						params->canon_props = realloc(params->canon_props, sizeof(params->canon_props[0])*(j+1));
+					else
+						params->canon_props = malloc(sizeof(params->canon_props[0]));
+					params->canon_props[j].type = type;
+					params->canon_props[j].proptype = proptype;
+					params->canon_props[j].size = size;
+					params->canon_props[j].data = malloc(size-PTP_ece_Prop_Val_Data);
+					memcpy(params->canon_props[j].data, data, size-PTP_ece_Prop_Val_Data);
+					memset (&params->canon_props[j].dpd,0,sizeof(params->canon_props[j].dpd));
+					params->canon_props[j].dpd.GetSet = 1;
+					params->canon_props[j].dpd.FormFlag = PTP_DPFF_None;
+					params->nrofcanon_props = j+1;
+				}
+				dpd = &params->canon_props[j].dpd;
+
+				/* fix GetSet value */
+				switch (proptype) {
+#define XX(x) case PTP_DPC_CANON_##x:
+					XX(EOS_FocusMode)
+					XX(EOS_BatteryPower)
+					XX(EOS_BatterySelect)
+					XX(EOS_ModelID)
+					XX(EOS_PTPExtensionVersion)
+					XX(EOS_DPOFVersion)
+					XX(EOS_AvailableShots)
+					XX(EOS_CurrentStorage)
+					XX(EOS_CurrentFolder)
+					XX(EOS_MyMenu)
+					XX(EOS_MyMenuList)
+					XX(EOS_HDDirectoryStructure)
+					XX(EOS_BatteryInfo)
+					XX(EOS_AdapterInfo)
+					XX(EOS_LensStatus)
+					XX(EOS_CardExtension)
+					XX(EOS_TempStatus)
+					XX(EOS_ShutterCounter)
+					XX(EOS_SerialNumber)
+					XX(EOS_DepthOfFieldPreview)
+					XX(EOS_EVFRecordStatus)
+					XX(EOS_LvAfSystem)
+					XX(EOS_FocusInfoEx)
+					XX(EOS_DepthOfField)
+					XX(EOS_Brightness)
+					XX(EOS_EFComp)
+					XX(EOS_LensName)
+					XX(EOS_LensID)
+#undef XX
+						dpd->GetSet = PTP_DPGS_Get;
+						break;
+				}
+
+				/* set DataType */
+				switch (proptype) {
+				case PTP_DPC_CANON_EOS_CameraTime:
+				case PTP_DPC_CANON_EOS_EVFOutputDevice:
+				case PTP_DPC_CANON_EOS_AvailableShots:
+				case PTP_DPC_CANON_EOS_CaptureDestination:
+				case PTP_DPC_CANON_EOS_WhiteBalanceXA:
+				case PTP_DPC_CANON_EOS_WhiteBalanceXB:
+				case PTP_DPC_CANON_EOS_QuickReviewTime:
+				case PTP_DPC_CANON_EOS_CurrentStorage:
+				case PTP_DPC_CANON_EOS_CurrentFolder:
+				case PTP_DPC_CANON_EOS_ShutterCounter:
+				case PTP_DPC_CANON_EOS_ModelID:
+				case PTP_DPC_CANON_EOS_LensID:
+				case PTP_DPC_CANON_EOS_StroboFiring:
+					dpd->DataType = PTP_DTC_UINT32;
+					break;
+				case PTP_DPC_CANON_EOS_Aperture:
+				case PTP_DPC_CANON_EOS_ShutterSpeed:
+				case PTP_DPC_CANON_EOS_ISOSpeed:
+				case PTP_DPC_CANON_EOS_FocusMode:
+				case PTP_DPC_CANON_EOS_AutoExposureMode:
+				case PTP_DPC_CANON_EOS_ColorSpace:
+				case PTP_DPC_CANON_EOS_BatteryPower:
+				case PTP_DPC_CANON_EOS_PTPExtensionVersion:
+				case PTP_DPC_CANON_EOS_DriveMode:
+				case PTP_DPC_CANON_EOS_AEB:
+					dpd->DataType = PTP_DTC_UINT16;
+					break;
+				case PTP_DPC_CANON_EOS_PictureStyle:
+				case PTP_DPC_CANON_EOS_WhiteBalance:
+				case PTP_DPC_CANON_EOS_MeteringMode:
+				case PTP_DPC_CANON_EOS_ExpCompensation: /* actually int8 if you calculate */
+					dpd->DataType = PTP_DTC_UINT8;
+					break;
+				case PTP_DPC_CANON_EOS_Owner:
+				case PTP_DPC_CANON_EOS_Artist:
+				case PTP_DPC_CANON_EOS_Copyright:
+				case PTP_DPC_CANON_EOS_SerialNumber:
+				case PTP_DPC_CANON_EOS_LensName:
+					dpd->DataType = PTP_DTC_STR;
+					break;
+				case PTP_DPC_CANON_EOS_WhiteBalanceAdjustA:
+				case PTP_DPC_CANON_EOS_WhiteBalanceAdjustB:
+					dpd->DataType = PTP_DTC_INT16;
+					break;
+				/* unknown props, listed from dump.... all 16 bit, but vals might be smaller */
+				case PTP_DPC_CANON_EOS_BatterySelect:
+				case 0xd114:
+				case PTP_DPC_CANON_EOS_DPOFVersion:
+				case PTP_DPC_CANON_EOS_BracketMode:
+					dpd->DataType = PTP_DTC_UINT16;
+					ptp_debug (params, "event %d: Unknown EOS property %04x, datasize is %d, using uint16", i ,proptype, size-PTP_ece_Prop_Val_Data);
+					for (j=0;j<size-PTP_ece_Prop_Val_Data;j++)
+						ptp_debug (params, "    %d: %02x", j, data[j]);
+					break;
+				case PTP_DPC_CANON_EOS_CustomFunc1:
+				case PTP_DPC_CANON_EOS_CustomFunc2:
+				case PTP_DPC_CANON_EOS_CustomFunc3:
+				case PTP_DPC_CANON_EOS_CustomFunc4:
+				case PTP_DPC_CANON_EOS_CustomFunc5:
+				case PTP_DPC_CANON_EOS_CustomFunc6:
+				case PTP_DPC_CANON_EOS_CustomFunc7:
+				case PTP_DPC_CANON_EOS_CustomFunc8:
+				case PTP_DPC_CANON_EOS_CustomFunc9:
+				case PTP_DPC_CANON_EOS_CustomFunc10:
+				case PTP_DPC_CANON_EOS_CustomFunc11:
+					dpd->DataType = PTP_DTC_UINT8;
+					ptp_debug (params, "event %d: Unknown EOS property %04x, datasize is %d, using uint8", i ,proptype, size-PTP_ece_Prop_Val_Data);
+					for (j=0;j<size-PTP_ece_Prop_Val_Data;j++)
+						ptp_debug (params, "    %d: %02x", j, data[j]);
+					/* custom func entries look like this on the 400D: '5 0 0 0 ?' = 4 bytes size + 1 byte data */
+					data += 4;
+					break;
+				/* yet unknown 32bit props */
+				case PTP_DPC_CANON_EOS_ColorTemperature:
+				case PTP_DPC_CANON_EOS_WftStatus:
+				case PTP_DPC_CANON_EOS_LensStatus:
+				case PTP_DPC_CANON_EOS_CardExtension:
+				case PTP_DPC_CANON_EOS_TempStatus:
+				case PTP_DPC_CANON_EOS_PhotoStudioMode:
+				case PTP_DPC_CANON_EOS_EVFMode:
+				case PTP_DPC_CANON_EOS_DepthOfFieldPreview:
+				case PTP_DPC_CANON_EOS_EVFSharpness:
+				case PTP_DPC_CANON_EOS_EVFWBMode:
+				case PTP_DPC_CANON_EOS_EVFClickWBCoeffs:
+				case PTP_DPC_CANON_EOS_EVFColorTemp:
+				case PTP_DPC_CANON_EOS_EVFRecordStatus:
+				case PTP_DPC_CANON_EOS_ExposureSimMode:
+				case PTP_DPC_CANON_EOS_LvAfSystem:
+				case PTP_DPC_CANON_EOS_MovSize:
+				case PTP_DPC_CANON_EOS_DepthOfField:
+				case PTP_DPC_CANON_EOS_LvViewTypeSelect:
+				case PTP_DPC_CANON_EOS_ImageFormat:
+				case PTP_DPC_CANON_EOS_ImageFormatCF:
+				case PTP_DPC_CANON_EOS_ImageFormatSD:
+				case PTP_DPC_CANON_EOS_ImageFormatExtHD:
+				case PTP_DPC_CANON_EOS_CustomFuncEx:
+//					dpd->DataType = PTP_DTC_UINT32;
+					ptp_debug (params, "event %d: Unknown EOS property %04x, datasize is %d, using uint32", i ,proptype, size-PTP_ece_Prop_Val_Data);
+					if ((size-PTP_ece_Prop_Val_Data) % sizeof(uint32_t) != 0)
+						ptp_debug (params, "event %d: Warning: datasize modulo sizeof(uint32) is not 0: ", i, (size-PTP_ece_Prop_Val_Data) % sizeof(uint32_t) );
+					for (j=0;j<(size-PTP_ece_Prop_Val_Data)/sizeof(uint32_t);j++)
+						ptp_debug (params, "    %d: 0x%8x", j, ((uint32_t*)data)[j]);
+					break;
+				default:
+					ptp_debug (params, "event %d: Unknown EOS property %04x, datasize is %d", i ,proptype, size-PTP_ece_Prop_Val_Data);
+					for (j=0;j<size-PTP_ece_Prop_Val_Data;j++)
+						ptp_debug (params, "    %d: %02x", j, data[j]);
+					break;
+				}
+				switch (dpd->DataType) {
+				case PTP_DTC_UINT32:
+					dpd->FactoryDefaultValue.u32	= dtoh32a(data);
+					dpd->CurrentValue.u32		= dtoh32a(data);
+					ptp_debug (params ,"event %d: currentvalue of %x is %x", i, proptype, dpd->CurrentValue.u32);
+					break;
+				case PTP_DTC_UINT16:
+					dpd->FactoryDefaultValue.u16	= dtoh16a(data);
+					dpd->CurrentValue.u16		= dtoh16a(data);
+					ptp_debug (params,"event %d: currentvalue of %x is %x", i, proptype, dpd->CurrentValue.u16);
+					break;
+				case PTP_DTC_UINT8:
+					dpd->FactoryDefaultValue.u8	= dtoh8a(data);
+					dpd->CurrentValue.u8		= dtoh8a(data);
+					ptp_debug (params,"event %d: currentvalue of %x is %x", i, proptype, dpd->CurrentValue.u8);
+					break;
+				case PTP_DTC_STR: {
+#if 0 /* 5D MII and 400D aktually store plain ASCII in their string properties */
+					uint8_t len = 0;
+					dpd->FactoryDefaultValue.str	= ptp_unpack_string(params, data, 0, &len);
+					dpd->CurrentValue.str		= ptp_unpack_string(params, data, 0, &len);
+#else
+					dpd->FactoryDefaultValue.str	= strdup( (char*)data );
+					dpd->CurrentValue.str		= strdup( (char*)data );
+#endif
+					ptp_debug (params,"event %d: currentvalue of %x is %s", i, proptype, dpd->CurrentValue.str);
+					break;
+				}
+				default:
+					/* debug is printed in switch above this one */
+					break;
+				}
+
+				/* ImageFormat special handling */
+				switch (proptype) {
+				case PTP_DPC_CANON_EOS_ImageFormat:
+				case PTP_DPC_CANON_EOS_ImageFormatCF:
+				case PTP_DPC_CANON_EOS_ImageFormatSD:
+				case PTP_DPC_CANON_EOS_ImageFormatExtHD:
+					dpd->DataType = PTP_DTC_UINT16;
+					dpd->FactoryDefaultValue.u16	= ptp_unpack_EOS_ImageFormat( params, &data );
+					dpd->CurrentValue.u16		= dpd->FactoryDefaultValue.u16;
+					ptp_debug (params,"event %d: currentvalue of %x is %x", i, proptype, dpd->CurrentValue.u8);
+					break;
+				}
+
+				break;
+		}
+		case 0: /* end marker */
+			if (size == 8) /* no output */
+				break;
+			ptp_debug (params, "event %d: EOS event 0, but size %d", i, size);
+			break;
+		default:
+			switch (type) {
+#define XX(x)		case PTP_EC_CANON_EOS_##x: ptp_debug (params, "event %d: unhandled EOS event "#x" (size %d)", i, size);break;
+			XX(RequestGetEvent)
+			XX(ObjectRemoved)
+			XX(RequestGetObjectInfoEx)
+			XX(StorageStatusChanged)
+			XX(StorageInfoChanged)
+			XX(ObjectInfoChangedEx)
+			XX(ObjectContentChanged)
+			XX(CameraStatusChanged)
+			XX(WillSoonShutdown)
+			XX(ShutdownTimerUpdated)
+			XX(RequestCancelTransfer)
+			XX(RequestObjectTransferDT)
+			XX(RequestCancelTransferDT)
+			XX(StoreAdded)
+			XX(StoreRemoved)
+			XX(BulbExposureTime)
+			XX(RecordingTime)
+			XX(RequestObjectTransferTS)
+			XX(AfResult)
+#undef XX
+			default:
+				ptp_debug (params, "event %d: unknown EOS event %04x", i, type);
+				break;
+			}
+			if (size >= 0x8) {	/* event info */
+				int j;
+				for (j=8;j<size;j++)
+					ptp_debug (params, "    %d: %02x", j, curdata[j]);
+			}
+			(*ce)[i].type = PTP_CANON_EOS_CHANGES_TYPE_UNKNOWN;
+			break;
+		}
+		curdata += size;
+		i++;
+		if ((size == 8) && (type == 0))
+			break;
+	}
+
+	return entries;
+}
+
+/*
+    PTP USB Event container unpack for Nikon events.
+*/
+#define PTP_nikon_ec_Length		0
+#define PTP_nikon_ec_Code		2
+#define PTP_nikon_ec_Param1		4
+#define PTP_nikon_ec_Size		6
+static inline void
+ptp_unpack_Nikon_EC (PTPParams *params, unsigned char* data, unsigned int len, PTPContainer **ec, int *cnt)
+{
+	int i;
+
+	*ec = NULL;
+	if (data == NULL)
+		return;
+	if (len < PTP_nikon_ec_Code)
+		return;
+	*cnt = dtoh16a(&data[PTP_nikon_ec_Length]);
+	if (*cnt > (len-PTP_nikon_ec_Code)/PTP_nikon_ec_Size) /* broken cnt? */
+		return;
+	*ec = malloc(sizeof(PTPContainer)*(*cnt));
+	
+	for (i=0;i<*cnt;i++) {
+		memset(&(*ec)[i],0,sizeof(PTPContainer));
+		(*ec)[i].Code	= dtoh16a(&data[PTP_nikon_ec_Code+PTP_nikon_ec_Size*i]);
+		(*ec)[i].Param1	= dtoh32a(&data[PTP_nikon_ec_Param1+PTP_nikon_ec_Size*i]);
+		(*ec)[i].Nparam	= 1;
+	}
+}
+
+
+static inline uint32_t
+ptp_pack_EK_text(PTPParams *params, PTPEKTextParams *text, unsigned char **data) {
+	int i, len = 0;
+	uint8_t	retlen;
+	unsigned char *curdata;
+
+	len =	2*(strlen(text->title)+1)+1+
+		2*(strlen(text->line[0])+1)+1+
+		2*(strlen(text->line[1])+1)+1+
+		2*(strlen(text->line[2])+1)+1+
+		2*(strlen(text->line[3])+1)+1+
+		2*(strlen(text->line[4])+1)+1+
+		4*2+2*4+2+4+2+5*4*2;
+	*data = malloc(len);
+	if (!*data) return 0;
+
+	curdata = *data;
+	htod16a(curdata,100);curdata+=2;
+	htod16a(curdata,1);curdata+=2;
+	htod16a(curdata,0);curdata+=2;
+	htod16a(curdata,1000);curdata+=2;
+
+	htod32a(curdata,0);curdata+=4;
+	htod32a(curdata,0);curdata+=4;
+
+	htod16a(curdata,6);curdata+=2;
+	htod32a(curdata,0);curdata+=4;
+
+	ptp_pack_string(params, text->title, curdata, 0, &retlen); curdata+=2*retlen+1;htod16a(curdata,0);curdata+=2;
+	htod16a(curdata,0x10);curdata+=2;
+	
+	for (i=0;i<5;i++) {
+		ptp_pack_string(params, text->line[i], curdata, 0, &retlen); curdata+=2*retlen+1;htod16a(curdata,0);curdata+=2;
+		htod16a(curdata,0x10);curdata+=2;
+		htod16a(curdata,0x01);curdata+=2;
+		htod16a(curdata,0x02);curdata+=2;
+		htod16a(curdata,0x06);curdata+=2;
+	}
+	return len;
+}
+
+#define ptp_canon_dir_version	0x00
+#define ptp_canon_dir_ofc	0x02
+#define ptp_canon_dir_unk1	0x04
+#define ptp_canon_dir_objectid	0x08
+#define ptp_canon_dir_parentid	0x0c
+#define ptp_canon_dir_previd	0x10	/* in same dir */
+#define ptp_canon_dir_nextid	0x14	/* in same dir */
+#define ptp_canon_dir_nextchild	0x18	/* down one dir */
+#define ptp_canon_dir_storageid	0x1c	/* only in storage entry */
+#define ptp_canon_dir_name	0x20
+#define ptp_canon_dir_flags	0x2c
+#define ptp_canon_dir_size	0x30
+#define ptp_canon_dir_unixtime	0x34
+#define ptp_canon_dir_year	0x38
+#define ptp_canon_dir_month	0x39
+#define ptp_canon_dir_mday	0x3a
+#define ptp_canon_dir_hour	0x3b
+#define ptp_canon_dir_minute	0x3c
+#define ptp_canon_dir_second	0x3d
+#define ptp_canon_dir_unk2	0x3e
+#define ptp_canon_dir_thumbsize	0x40
+#define ptp_canon_dir_width	0x44
+#define ptp_canon_dir_height	0x48
+
+static inline uint16_t
+ptp_unpack_canon_directory (
+	PTPParams		*params,
+	unsigned char		*dir,
+	uint32_t		cnt,
+	PTPObjectHandles	*handles,
+	PTPObjectInfo		**oinfos,	/* size(handles->n) */
+	uint32_t		**flags		/* size(handles->n) */
+) {
+	unsigned int	i, j, nrofobs = 0, curob = 0;
+
+#define ISOBJECT(ptr) (dtoh32a((ptr)+ptp_canon_dir_storageid) == 0xffffffff)
+	for (i=0;i<cnt;i++)
+		if (ISOBJECT(dir+i*0x4c)) nrofobs++;
+	handles->n = nrofobs;
+	handles->Handler = calloc(sizeof(handles->Handler[0]),nrofobs);
+	if (!handles->Handler) return PTP_RC_GeneralError;
+	*oinfos = calloc(sizeof((*oinfos)[0]),nrofobs);
+	if (!*oinfos) return PTP_RC_GeneralError;
+	*flags  = calloc(sizeof((*flags)[0]),nrofobs);
+	if (!*flags) return PTP_RC_GeneralError;
+
+	/* Migrate data into objects ids, handles into
+	 * the object handler array.
+	 */
+	curob = 0;
+	for (i=0;i<cnt;i++) {
+		unsigned char	*cur = dir+i*0x4c;
+		PTPObjectInfo	*oi = (*oinfos)+curob;
+
+		if (!ISOBJECT(cur))
+			continue;
+
+		handles->Handler[curob] = dtoh32a(cur + ptp_canon_dir_objectid);
+		oi->StorageID		= 0xffffffff;
+		oi->ObjectFormat	= dtoh16a(cur + ptp_canon_dir_ofc);
+		oi->ParentObject	= dtoh32a(cur + ptp_canon_dir_parentid);
+		oi->Filename		= strdup((char*)(cur + ptp_canon_dir_name));
+		oi->ObjectCompressedSize= dtoh32a(cur + ptp_canon_dir_size);
+		oi->ThumbCompressedSize	= dtoh32a(cur + ptp_canon_dir_thumbsize);
+		oi->ImagePixWidth	= dtoh32a(cur + ptp_canon_dir_width);
+		oi->ImagePixHeight	= dtoh32a(cur + ptp_canon_dir_height);
+		oi->CaptureDate		= oi->ModificationDate = dtoh32a(cur + ptp_canon_dir_unixtime);
+		(*flags)[curob]		= dtoh32a(cur + ptp_canon_dir_flags);
+		curob++;
+	}
+	/* Walk over Storage ID entries and distribute the IDs to
+	 * the parent objects. */
+	for (i=0;i<cnt;i++) {
+		unsigned char	*cur = dir+i*0x4c;
+		uint32_t	nextchild = dtoh32a(cur + ptp_canon_dir_nextchild);
+
+		if (ISOBJECT(cur))
+			continue;
+		for (j=0;j<handles->n;j++) if (nextchild == handles->Handler[j]) break;
+		if (j == handles->n) continue;
+		(*oinfos)[j].StorageID = dtoh32a(cur + ptp_canon_dir_storageid);
+	}
+	/* Walk over all objects and distribute the storage ids */
+	while (1) {
+		int changed = 0;
+		for (i=0;i<cnt;i++) {
+			unsigned char	*cur = dir+i*0x4c;
+			uint32_t	oid = dtoh32a(cur + ptp_canon_dir_objectid);
+			uint32_t	nextoid = dtoh32a(cur + ptp_canon_dir_nextid);
+			uint32_t	nextchild = dtoh32a(cur + ptp_canon_dir_nextchild);
+			uint32_t	storageid;
+
+			if (!ISOBJECT(cur))
+				continue;
+			for (j=0;j<handles->n;j++) if (oid == handles->Handler[j]) break;
+			if (j == handles->n) {
+				/*fprintf(stderr,"did not find oid in lookup pass for current oid\n");*/
+				continue;
+			}
+	 		storageid = (*oinfos)[j].StorageID;
+			if (storageid == 0xffffffff) continue;
+			if (nextoid != 0xffffffff) {
+				for (j=0;j<handles->n;j++) if (nextoid == handles->Handler[j]) break;
+				if (j == handles->n) {
+					/*fprintf(stderr,"did not find oid in lookup pass for next oid\n");*/
+					continue;
+				}
+				if ((*oinfos)[j].StorageID == 0xffffffff) {
+					(*oinfos)[j].StorageID = storageid;
+					changed++;
+				}
+			}
+			if (nextchild != 0xffffffff) {
+				for (j=0;j<handles->n;j++) if (nextchild == handles->Handler[j]) break;
+				if (j == handles->n) {
+					/*fprintf(stderr,"did not find oid in lookup pass for next child\n");*/
+					continue;
+				}
+				if ((*oinfos)[j].StorageID == 0xffffffff) {
+					(*oinfos)[j].StorageID = storageid;
+					changed++;
+				}
+			}
+		}
+		/* Check if we:
+		 * - changed no entry (nothing more to do)
+		 * - changed all of them at once (usually happens)
+		 * break if we do.
+		 */
+		if (!changed || (changed==nrofobs-1))
+			break;
+	}
+#undef ISOBJECT
+	return PTP_RC_OK;
+}
diff --git a/src/ptp.c b/src/ptp.c
new file mode 100644
index 0000000..a8c265b
--- /dev/null
+++ b/src/ptp.c
@@ -0,0 +1,4974 @@
+/* ptp.c
+ *
+ * Copyright (C) 2001-2004 Mariusz Woloszyn <emsi@ipartners.pl>
+ * Copyright (C) 2003-2009 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>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define _BSD_SOURCE
+#include "config.h"
+#include "ptp.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef ENABLE_NLS
+#  include <libintl.h>
+#  undef _
+#  define _(String) dgettext (PACKAGE, String)
+#  ifdef gettext_noop
+#    define N_(String) gettext_noop (String)
+#  else
+#    define N_(String) (String)
+#  endif
+#else
+#  define textdomain(String) (String)
+#  define gettext(String) (String)
+#  define dgettext(Domain,Message) (Message)
+#  define dcgettext(Domain,Message,Type) (Message)
+#  define bindtextdomain(Domain,Directory) (Domain)
+#  define _(String) (String)
+#  define N_(String) (String)
+#endif
+
+#define CHECK_PTP_RC(result)	{uint16_t r=(result); if (r!=PTP_RC_OK) return r;}
+
+#define PTP_CNT_INIT(cnt) {memset(&cnt,0,sizeof(cnt));}
+
+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);
+
+static 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);
+}  
+
+static 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);
+}
+
+/* Pack / unpack functions */
+
+#include "ptp-pack.c"
+
+/* major PTP functions */
+
+/* Transaction data phase description */
+#define PTP_DP_NODATA		0x0000	/* no data phase */
+#define PTP_DP_SENDDATA		0x0001	/* sending data */
+#define PTP_DP_GETDATA		0x0002	/* receiving data */
+#define PTP_DP_DATA_MASK	0x00ff	/* data phase mask */
+
+/**
+ * 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 thether 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 beeing ignored (no
+ * dataphase).
+ * The memory for a pointer should be preserved by the caller, if data are
+ * beeing retreived the appropriate amount of memory is beeing 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.
+ **/
+static uint16_t
+ptp_transaction_new (PTPParams* params, PTPContainer* ptp, 
+		uint16_t flags, unsigned int sendlen,
+		PTPDataHandler *handler
+) {
+	int tries;
+
+	if ((params==NULL) || (ptp==NULL)) 
+		return PTP_ERROR_BADPARAM;
+
+	ptp->Transaction_ID=params->transaction_id++;
+	ptp->SessionID=params->session_id;
+	/* send request */
+	CHECK_PTP_RC(params->sendreq_func (params, ptp));
+	/* is there a dataphase? */
+	switch (flags&PTP_DP_DATA_MASK) {
+	case PTP_DP_SENDDATA:
+		{
+			uint16_t ret;
+			ret = params->senddata_func(params, ptp,
+						    sendlen, handler);
+			if (ret == PTP_ERROR_CANCEL) {
+				ret = params->cancelreq_func(params, 
+							     params->transaction_id-1);
+				if (ret == PTP_RC_OK)
+					ret = PTP_ERROR_CANCEL;
+			}
+			if (ret != PTP_RC_OK)
+				return ret;
+		}
+		break;
+	case PTP_DP_GETDATA:
+		{
+			uint16_t ret;
+			ret = params->getdata_func(params, ptp, handler);
+			if (ret == PTP_ERROR_CANCEL) {
+				ret = params->cancelreq_func(params, 
+							     params->transaction_id-1);
+				if (ret == PTP_RC_OK)
+					ret = PTP_ERROR_CANCEL;
+			}
+			if (ret != PTP_RC_OK)
+				return ret;
+		}
+		break;
+	case PTP_DP_NODATA:
+		break;
+	default:
+		return PTP_ERROR_BADPARAM;
+	}
+	tries = 3;
+	while (1) {
+		/* get response */
+		CHECK_PTP_RC(params->getresp_func(params, ptp));
+		if (ptp->Transaction_ID != params->transaction_id-1) {
+			/* try to clean up potential left overs from previous session */
+			if ((ptp->Code == PTP_OC_OpenSession) && tries--)
+				continue;
+			ptp_error (params,
+				"PTP: Sequence number mismatch %d vs expected %d.",
+				ptp->Transaction_ID, params->transaction_id-1
+			);
+			return PTP_ERROR_BADPARAM;
+		}
+		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,
+	       unsigned long *putlen
+) {
+	PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;
+
+	if (priv->curoff + sendlen > priv->size) {
+		priv->data = realloc (priv->data, priv->curoff+sendlen);
+		priv->size = priv->curoff + sendlen;
+	}
+	memcpy (priv->data + priv->curoff, data, sendlen);
+	priv->curoff += sendlen;
+	*putlen = 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));
+	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,
+	       unsigned long *putlen
+) {
+	int		written;
+	PTPFDHandlerPrivate* priv = (PTPFDHandlerPrivate*)private;
+
+	written = write (priv->fd, data, sendlen);
+	if (written != -1)
+		*putlen = written;
+	else
+		return PTP_RC_GeneralError;
+	return PTP_RC_OK;
+}
+
+static uint16_t
+ptp_init_fd_handler(PTPDataHandler *handler, int fd) {
+	PTPFDHandlerPrivate* priv;
+	priv = malloc (sizeof(PTPFDHandlerPrivate));
+	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 */
+static uint16_t
+ptp_transaction (PTPParams* params, PTPContainer* ptp, 
+		uint16_t flags, unsigned int sendlen,
+		unsigned char **data, unsigned int *recvlen
+) {
+	PTPDataHandler	handler;
+	uint16_t	ret;
+
+	switch (flags & PTP_DP_DATA_MASK) {
+	case PTP_DP_SENDDATA:
+		ptp_init_send_memory_handler (&handler, *data, sendlen);
+		break;
+	case PTP_DP_GETDATA:
+		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 (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)
+{
+	uint16_t 	ret;
+	unsigned long	len;
+	PTPContainer	ptp;
+	unsigned char*	di=NULL;
+	PTPDataHandler	handler;
+
+	ptp_init_recv_memory_handler (&handler);
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetDeviceInfo;
+	ptp.Nparam=0;
+	len=0;
+	ret=ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, &handler);
+	ptp_exit_recv_memory_handler (&handler, &di, &len);
+	if (!di) ret = PTP_RC_GeneralError;
+	if (ret == PTP_RC_OK) ptp_unpack_DI(params, di, deviceinfo, len);
+	free(di);
+	return ret;
+}
+
+uint16_t
+ptp_canon_eos_getdeviceinfo (PTPParams* params, PTPCanonEOSDeviceInfo*di)
+{
+	uint16_t 	ret;
+	PTPContainer	ptp;
+	PTPDataHandler	handler;
+	unsigned long	len;
+	unsigned char	*data;
+
+	ptp_init_recv_memory_handler (&handler);
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_EOS_GetDeviceInfoEx;
+	ptp.Nparam=0;
+	len=0;
+	data=NULL;
+	ret=ptp_transaction_new(params, &ptp, PTP_DP_GETDATA, 0, &handler);
+	ptp_exit_recv_memory_handler (&handler, &data, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_EOS_DI(params, data, di, len);
+	free (data);
+	return ret;
+}
+
+/**
+ * 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;
+	int i;
+
+	if( n_param > 5 )
+		return PTP_RC_InvalidParameter;
+
+	PTP_CNT_INIT(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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+
+	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.Code=PTP_OC_OpenSession;
+	ptp.Param1=session;
+	ptp.Nparam=1;
+	ret=ptp_transaction_new(params, &ptp, PTP_DP_NODATA, 0, NULL);
+	/* now set the global session id to current session number */
+	params->session_id=session;
+	return ret;
+}
+
+/**
+ * ptp_free_params:
+ * params:	PTPParams*
+ *
+ * Frees all data within the PTPParams struct.
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+void
+ptp_free_params (PTPParams *params) {
+	int i;
+
+	if (params->cameraname) free (params->cameraname);
+	if (params->wifi_profiles) free (params->wifi_profiles);
+	for (i=0;i<params->nrofobjects;i++)
+		ptp_free_object (&params->objects[i]);
+	free (params->objects);
+	ptp_free_DI (&params->deviceinfo);
+}
+
+/**
+ * ptp_getststorageids:
+ * params:	PTPParams*
+ *
+ * Gets array of StorageIDs and fills the storageids structure.
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+uint16_t
+ptp_getstorageids (PTPParams* params, PTPStorageIDs* storageids)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned int len;
+	unsigned char* sids=NULL;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetStorageIDs;
+	ptp.Nparam=0;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &sids, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_SIDs(params, sids, storageids, len);
+	free(sids);
+	return ret;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* si=NULL;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetStorageInfo;
+	ptp.Param1=storageid;
+	ptp.Nparam=1;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &si, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_SI(params, si, storageinfo, len);
+	free(si);
+	return ret;
+}
+
+/**
+ * ptp_getobjecthandles:
+ * params:	PTPParams*
+ *		storage			- StorageID
+ *		objectformatcode	- ObjectFormatCode (optional)
+ *		associationOH		- ObjectHandle of Association for
+ *					  wich 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* oh=NULL;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetObjectHandles;
+	ptp.Param1=storage;
+	ptp.Param2=objectformatcode;
+	ptp.Param3=associationOH;
+	ptp.Nparam=3;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &oh, &len);
+	if (ret == PTP_RC_OK) {
+		ptp_unpack_OH(params, oh, objecthandles, len);
+	} 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".
+			 */
+			objecthandles->Handler = NULL;
+			objecthandles->n = 0;
+			ret = PTP_RC_OK;
+		}
+	}
+	free(oh);
+	return ret;
+}
+
+/**
+ * ptp_getnumobjects:
+ * params:	PTPParams*
+ *		storage			- StorageID
+ *		objectformatcode	- ObjectFormatCode (optional)
+ *		associationOH		- ObjectHandle of Association for
+ *					  wich 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetNumObjects;
+	ptp.Param1=storage;
+	ptp.Param2=objectformatcode;
+	ptp.Param3=associationOH;
+	ptp.Nparam=3;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
+	if (ret == PTP_RC_OK) {
+		if (ptp.Nparam >= 1)
+			*numobs = ptp.Param1;
+		else
+			ret = PTP_RC_GeneralError;
+	}
+	return ret;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* oi=NULL;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetObjectInfo;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &oi, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_OI(params, oi, objectinfo, len);
+	free(oi);
+	return ret;
+}
+
+/**
+ * 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;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetObject;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	len=0;
+	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, &len);
+}
+
+/**
+ * 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.Code=PTP_OC_GetObject;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	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_init_fd_handler (&handler, fd);
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetObject;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	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
+ *
+ * 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)
+{
+	PTPContainer ptp;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetPartialObject;
+	ptp.Param1=handle;
+	ptp.Param2=offset;
+	ptp.Param3=maxbytes;
+	ptp.Nparam=3;
+	len=0;
+	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)
+{
+	PTPContainer ptp;
+	unsigned int len;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetThumb;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, object, &len);
+}
+
+/**
+ * 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;
+	uint16_t ret;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_DeleteObject;
+	ptp.Param1=handle;
+	ptp.Param2=ofc;
+	ptp.Nparam=2;
+	ret = ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
+	if (ret != PTP_RC_OK) {
+		return ret;
+	}
+	/* If the object is cached and could be removed, cleanse cache. */
+	ptp_remove_object_from_cache(params, handle);
+	return PTP_RC_OK;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* oidata=NULL;
+	uint32_t size;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_SendObjectInfo;
+	ptp.Param1=*store;
+	ptp.Param2=*parenthandle;
+	ptp.Nparam=2;
+	
+	size=ptp_pack_OI(params, objectinfo, &oidata);
+	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &oidata, NULL); 
+	free(oidata);
+	*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
+ *		uint32_t size		- object size
+ *		
+ * Sends object to Responder.
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ */
+uint16_t
+ptp_sendobject (PTPParams* params, unsigned char* object, uint32_t size)
+{
+	PTPContainer ptp;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_SendObject;
+	ptp.Nparam=0;
+
+	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &object, NULL);
+}
+
+/**
+ * ptp_sendobject_from_handler:
+ * params:	PTPParams*
+ *		PTPDataHandler*         - File descriptor to read() object from
+ *              uint32_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, uint32_t size)
+{
+	PTPContainer	ptp;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_SendObject;
+	ptp.Nparam=0;
+	return ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, handler);
+}
+
+
+/**
+ * ptp_sendobject_fromfd:
+ * params:	PTPParams*
+ *		fd                      - File descriptor to read() object from
+ *              uint32_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, uint32_t size)
+{
+	PTPContainer	ptp;
+	PTPDataHandler	handler;
+	uint16_t	ret;
+
+	ptp_init_fd_handler (&handler, fd);
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_SendObject;
+	ptp.Nparam=0;
+	ret = ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, &handler);
+	ptp_exit_fd_handler (&handler);
+	return ret;
+}
+
+
+uint16_t
+ptp_getdevicepropdesc (PTPParams* params, uint16_t propcode, 
+			PTPDevicePropDesc* devicepropertydesc)
+{
+	PTPContainer ptp;
+	uint16_t ret;
+	unsigned int len;
+	unsigned char* dpd=NULL;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetDevicePropDesc;
+	ptp.Param1=propcode;
+	ptp.Nparam=1;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &dpd, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_DPD(params, dpd, devicepropertydesc, len);
+	free(dpd);
+	return ret;
+}
+
+
+uint16_t
+ptp_getdevicepropvalue (PTPParams* params, uint16_t propcode,
+			PTPPropertyValue* value, uint16_t datatype)
+{
+	PTPContainer ptp;
+	uint16_t ret;
+	unsigned int len;
+	int offset;
+	unsigned char* dpv=NULL;
+
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_GetDevicePropValue;
+	ptp.Param1=propcode;
+	ptp.Nparam=1;
+	len=offset=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &dpv, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_DPV(params, dpv, &offset, len, value, datatype);
+	free(dpv);
+	return ret;
+}
+
+uint16_t
+ptp_setdevicepropvalue (PTPParams* params, uint16_t propcode,
+			PTPPropertyValue *value, uint16_t datatype)
+{
+	PTPContainer ptp;
+	uint16_t ret;
+	uint32_t size;
+	unsigned char* dpv=NULL;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_SetDevicePropValue;
+	ptp.Param1=propcode;
+	ptp.Nparam=1;
+	size=ptp_pack_DPV(params, value, &dpv, datatype);
+	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &dpv, NULL);
+	free(dpv);
+	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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* oidata=NULL;
+	uint32_t size;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_EK_SendFileObjectInfo;
+	ptp.Param1=*store;
+	ptp.Param2=*parenthandle;
+	ptp.Nparam=2;
+	
+	size=ptp_pack_OI(params, objectinfo, &oidata);
+	ret=ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &oidata, NULL); 
+	free(oidata);
+	*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.Code   = PTP_OC_EK_GetSerial;
+	ptp.Nparam = 0;
+	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.Code   = PTP_OC_EK_SetSerial;
+	ptp.Nparam = 0;
+	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);
+	ptp.Code   = 0x9007;
+	ptp.Nparam = 0;
+	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;
+	uint16_t	ret;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = 0x9009;
+	ptp.Nparam = 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL); 
+	*p1 = ptp.Param1;
+	*p2 = ptp.Param2;
+	return ret;
+}
+
+/* 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);
+	ptp.Code   = 0x900c;
+	ptp.Nparam = 0;
+	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 int size;
+	unsigned char *data;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = PTP_OC_EK_SetText;
+	ptp.Nparam = 0;
+	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.Code=PTP_OC_EK_SendFileObject;
+	ptp.Nparam=0;
+
+	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.Code=PTP_OC_EK_SendFileObject;
+	ptp.Nparam=0;
+	return ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, handler);
+}
+
+/*************************************************************************
+ *
+ * Canon PTP extensions support
+ *
+ * (C) Nikolai Kopanygin 2003
+ *
+ *************************************************************************/
+
+
+/**
+ * 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) 
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetPartialObjectInfo;
+	ptp.Param1=handle;
+	ptp.Param2=p2;
+	ptp.Nparam=2;
+	ret=ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
+	*size=ptp.Param1;
+	*rp2=ptp.Param2;
+	return ret;
+}
+
+/**
+ * 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;
+	unsigned int size = 0;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetMACAddress;
+	ptp.Nparam=0;
+	*mac = NULL;
+	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, mac, &size);
+}
+
+/**
+ * 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->n) */
+	uint32_t		**flags		/* size(handles->n) */
+) {
+	PTPContainer	ptp;
+	unsigned char	*dir = NULL;
+	unsigned int	size = 0;
+	uint16_t	ret;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetDirectory;
+	ptp.Nparam=0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &dir, &size);
+	if (ret != PTP_RC_OK)
+		return ret;
+	ret = ptp_unpack_canon_directory(params, dir, ptp.Param1, handles, oinfos, flags);
+	free (dir);
+	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;
+	uint16_t ret;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = PTP_OC_CANON_GetTreeInfo;
+	ptp.Nparam = 1;
+	ptp.Param1 = 0xf;
+	ret = ptp_transaction(params, &ptp, PTP_DP_NODATA, 0, NULL, NULL);
+	if ((ret == PTP_RC_OK) && (ptp.Nparam>0))
+		*out = ptp.Param1;
+	return ret;
+}
+
+/**
+ * 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;
+	uint16_t ret;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = PTP_OC_CANON_GetPairingInfo;
+	ptp.Nparam = 1;
+	ptp.Param1 = nr;
+	*data = NULL;
+	*size = 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
+	if (ret != PTP_RC_OK)
+		return ret;
+	return PTP_RC_OK;
+}
+
+/**
+ * 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;
+	uint16_t ret;
+	unsigned char *out = NULL, *cur;
+	int i;
+	unsigned int size;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = PTP_OC_CANON_GetTreeSize;
+	ptp.Nparam = 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &out, &size);
+	if (ret != PTP_RC_OK)
+		return ret;
+	*cnt = dtoh32a(out);
+	*entries = malloc(sizeof(PTPCanon_directtransfer_entry)*(*cnt));
+	cur = out+4;
+	for (i=0;i<*cnt;i++) {
+		unsigned char len;
+		(*entries)[i].oid = dtoh32a(cur);
+		(*entries)[i].str = ptp_unpack_string(params, cur, 4, &len);
+		cur += 4+(cur[4]*2+1);
+	}
+	free (out);
+	return PTP_RC_OK;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char *evdata = NULL;
+	unsigned int len;
+	
+	*isevent=0;
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_CheckEvent;
+	ptp.Nparam=0;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &evdata, &len);
+	if (evdata!=NULL) {
+		if (ret == PTP_RC_OK) {
+        		ptp_unpack_EC(params, evdata, event, len);
+    			*isevent=1;
+        	}
+		free(evdata);
+	}
+	return ret;
+}
+
+uint16_t
+ptp_check_event (PTPParams *params) {
+	PTPContainer		event;
+	uint16_t		ret;
+
+	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_NIKON) &&
+		ptp_operation_issupported(params, PTP_OC_NIKON_CheckEvent)
+	) {
+		int evtcnt;
+		PTPContainer	*xevent = NULL;
+
+		ret = ptp_nikon_check_event(params, &xevent, &evtcnt);
+		if (ret != PTP_RC_OK)
+			return ret;
+
+		if (evtcnt) {
+			if (params->nrofevents)
+				params->events = realloc(params->events, sizeof(PTPContainer)*(evtcnt+params->nrofevents));
+			else
+				params->events = malloc(sizeof(PTPContainer)*evtcnt);
+			memcpy (&params->events[params->nrofevents],xevent,evtcnt*sizeof(PTPContainer));
+			params->nrofevents += evtcnt;
+			free (xevent);
+		}
+		return PTP_RC_OK;
+	}
+	if (	(params->deviceinfo.VendorExtensionID == PTP_VENDOR_CANON) &&
+		ptp_operation_issupported(params, PTP_OC_CANON_CheckEvent)
+	) {
+		int isevent;
+
+		ret = ptp_canon_checkevent (params,&event,&isevent);
+		if (ret!=PTP_RC_OK)
+			return ret;
+		if (isevent)
+			goto store_event;
+		/* FIXME: fallthrough or return? */
+	}
+	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);
+		if (params->nrofevents)
+			params->events = realloc(params->events, sizeof(PTPContainer)*(params->nrofevents+1));
+		else
+			params->events = malloc(sizeof(PTPContainer)*1);
+		memcpy (&params->events[params->nrofevents],&event,1*sizeof(PTPContainer));
+		params->nrofevents += 1;
+	}
+	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->nrofevents)
+		return 0;
+	memcpy (event, params->events, sizeof(PTPContainer));
+	memmove (params->events, params->events+1, sizeof(PTPContainer)*(params->nrofevents-1));
+	/* do not realloc on shrink. */
+	params->nrofevents--;
+	return 1;
+}
+
+/**
+ * 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, PTPCanon_changes_entry **entries, int *nrofentries)
+{
+	PTPContainer ptp;
+	uint16_t	ret;
+	unsigned int 	size = 0;
+	unsigned char	*data = NULL;
+
+	*nrofentries = 0;
+	*entries = NULL;
+	PTP_CNT_INIT(ptp);
+	ptp.Code = PTP_OC_CANON_EOS_GetEvent;
+	ptp.Nparam = 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	if (ret != PTP_RC_OK) return ret;
+        *nrofentries = ptp_unpack_CANON_changes(params,data,size,entries);
+	return PTP_RC_OK;
+}
+
+uint16_t
+ptp_canon_eos_getdevicepropdesc (PTPParams* params, uint16_t propcode,
+	PTPDevicePropDesc *dpd)
+{
+	int i;
+
+	for (i=0;i<params->nrofcanon_props;i++)
+		if (params->canon_props[i].proptype == propcode)
+			break;
+	if (params->nrofcanon_props == i)
+		return PTP_RC_Undefined;
+	memcpy (dpd, &params->canon_props[i].dpd, sizeof (*dpd));
+	if (dpd->FormFlag == PTP_DPFF_Enumeration) {
+		/* need to duplicate the Enumeration alloc */
+		dpd->FORM.Enum.SupportedValue = malloc (sizeof (PTPPropertyValue)*dpd->FORM.Enum.NumberOfValues);
+		memcpy (dpd->FORM.Enum.SupportedValue,
+			params->canon_props[i].dpd.FORM.Enum.SupportedValue,
+			sizeof (PTPPropertyValue)*dpd->FORM.Enum.NumberOfValues
+		);
+	}
+	if (dpd->DataType == PTP_DTC_STR) {
+		dpd->FactoryDefaultValue.str = strdup( params->canon_props[i].dpd.FactoryDefaultValue.str );
+		dpd->CurrentValue.str = strdup( params->canon_props[i].dpd.CurrentValue.str );
+	}
+
+	return PTP_RC_OK;
+}
+
+
+uint16_t
+ptp_canon_eos_getstorageids (PTPParams* params, PTPStorageIDs* storageids)
+{
+	PTPContainer	ptp;
+	unsigned int	len = 0;
+	uint16_t	ret;
+	unsigned char*	sids=NULL;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code 	= PTP_OC_CANON_EOS_GetStorageIDs;
+	ptp.Nparam	= 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &sids, &len);
+	if (ret == PTP_RC_OK) ptp_unpack_SIDs(params, sids, storageids, len);
+	free(sids);
+	return ret;
+}
+
+uint16_t
+ptp_canon_eos_getstorageinfo (PTPParams* params, uint32_t p1)
+{
+	PTPContainer ptp;
+	unsigned char	*data = NULL;
+	unsigned int	size = 0;
+	uint16_t	ret;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code 	= PTP_OC_CANON_EOS_GetStorageInfo;
+	ptp.Nparam	= 1;
+	ptp.Param1	= p1;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	/* FIXME: do stuff with data */
+	return ret;
+}
+
+/**
+ * ptp_canon_eos_getpartialobject:
+ * 
+ * This retrieves a part of an PTP object which you specify as object id.
+ * The id originates from 0x9116 call.
+ * After finishing it, we seem to need to call ptp_canon_eos_enddirecttransfer.
+ *
+ * params:	PTPParams*
+ * 		oid		Object ID
+ * 		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 oid, uint32_t offset, uint32_t xsize, unsigned char**data)
+{
+	PTPContainer	ptp;
+	unsigned int	size = 0;
+
+	*data = NULL;
+	PTP_CNT_INIT(ptp);
+	ptp.Code 	= PTP_OC_CANON_EOS_GetPartialObject;
+	ptp.Nparam	= 3;
+	ptp.Param1	= oid;
+	ptp.Param2	= offset;
+	ptp.Param3	= xsize;
+	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, &size);
+}
+
+uint16_t
+ptp_canon_eos_setdevicepropvalueex (PTPParams* params, unsigned char* data, unsigned int size)
+{
+	PTPContainer	ptp;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code 	= PTP_OC_CANON_EOS_SetDevicePropValueEx;
+	ptp.Nparam	= 0;
+	return ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
+}
+
+uint16_t
+ptp_canon_eos_setdevicepropvalue (PTPParams* params,
+	uint16_t propcode, PTPPropertyValue *value, uint16_t datatype
+) {
+	PTPContainer	ptp;
+	uint16_t	ret;
+	int 		i;
+	unsigned char	*data;
+	unsigned int	size;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code 	= PTP_OC_CANON_EOS_SetDevicePropValueEx;
+	ptp.Nparam	= 0;
+	for (i=0;i<params->nrofcanon_props;i++)
+		if (params->canon_props[i].proptype == propcode)
+			break;
+	if (params->nrofcanon_props == i)
+		return PTP_RC_Undefined;
+
+	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 );
+		params->canon_props[i].dpd.CurrentValue.u16 = value->u16;
+		ptp_pack_EOS_ImageFormat( params, data + 8, value->u16 );
+		break;
+	default:
+		if (datatype != PTP_DTC_STR) {
+			data = calloc(sizeof(uint32_t),3);
+			size = sizeof(uint32_t)*3;
+		} else {
+			size = strlen(value->str) + 1 + 8;
+			data = calloc(sizeof(char),size);
+		}
+		switch (datatype) {
+		case PTP_DTC_UINT8:
+			/*fprintf (stderr, "%x -> %d\n", propcode, value->u8);*/
+			htod8a(&data[8], value->u8);
+			params->canon_props[i].dpd.CurrentValue.u8 = value->u8;
+			break;
+		case PTP_DTC_UINT16:
+			/*fprintf (stderr, "%x -> %d\n", propcode, value->u16);*/
+			htod16a(&data[8], value->u16);
+			params->canon_props[i].dpd.CurrentValue.u16 = value->u16;
+			break;
+		case PTP_DTC_UINT32:
+			/*fprintf (stderr, "%x -> %d\n", propcode, value->u32);*/
+			htod32a(&data[8], value->u32);
+			params->canon_props[i].dpd.CurrentValue.u32 = value->u32;
+			break;
+		case PTP_DTC_STR:
+			strcpy((char*)data + 8, value->str);
+			free (params->canon_props[i].dpd.CurrentValue.str);
+			params->canon_props[i].dpd.CurrentValue.str = strdup(value->str);
+			break;
+		}
+	}
+
+	htod32a(&data[0], size);
+	htod32a(&data[4], propcode);
+
+	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL);
+	free (data);
+	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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char *data=NULL;
+	unsigned int len;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetPartialObjectEx;
+	ptp.Param1=handle;
+	ptp.Param2=offset;
+	ptp.Param3=size;
+	ptp.Param4=pos;
+	ptp.Nparam=4;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &len);
+	if (ret==PTP_RC_OK) {
+		*block=data;
+		*readnum=ptp.Param1;
+	}
+	return ret;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned int len;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetViewfinderImage;
+	ptp.Nparam=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, image, &len);
+	if (ret==PTP_RC_OK) *size=ptp.Param1;
+	return ret;
+}
+
+/**
+ * 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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* data=NULL;
+	unsigned int len;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetChanges;
+	ptp.Nparam=0;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &len);
+	if (ret == PTP_RC_OK)
+        	*propnum=ptp_unpack_uint16_t_array(params,data,0,props);
+	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)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char *data = NULL;
+	unsigned int len;
+	
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_CANON_GetObjectInfoEx;
+	ptp.Param1=store;
+	ptp.Param2=p2;
+	ptp.Param3=parent;
+	ptp.Param4=handle;
+	ptp.Nparam=4;
+	len=0;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &len);
+	if (ret == PTP_RC_OK) {
+		int i;
+		*entnum=ptp.Param1;
+		*entries=calloc(*entnum, sizeof(PTPCANONFolderEntry));
+		if (*entries!=NULL) {
+			for(i=0; i<(*entnum); i++)
+				ptp_unpack_Canon_FE(params,
+					data+i*PTP_CANON_FolderEntryLen,
+					&((*entries)[i]) );
+		} else {
+			ret=PTP_ERROR_IO; /* Cannot allocate memory */
+		}
+	}
+	free(data);
+	return ret;
+}
+
+/**
+ * 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 *oid - PTP object id.
+ *
+ **/
+uint16_t
+ptp_canon_get_objecthandle_by_name (PTPParams* params, char* name, uint32_t* objectid)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char *data = NULL;
+	uint8_t len;
+
+	PTP_CNT_INIT (ptp);
+	ptp.Code=PTP_OC_CANON_GetObjectHandleByName;
+	ptp.Nparam=0;
+	len=0;
+	data = malloc (2*(strlen(name)+1)+2);
+	memset (data, 0, 2*(strlen(name)+1)+2);
+	ptp_pack_string (params, name, data, 0, &len);
+	ret=ptp_transaction (params, &ptp, PTP_DP_SENDDATA, (len+1)*2+1, &data, NULL);
+	free (data);
+	*objectid = 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;
+
+	*data = NULL;
+	*size = 0;
+	PTP_CNT_INIT(ptp);
+	ptp.Code	= PTP_OC_CANON_GetCustomizeData;
+	ptp.Param1	= themenr;
+	ptp.Nparam	= 1;
+	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;
+	*data = NULL;
+	*size = 0;
+	PTP_CNT_INIT(ptp);
+	ptp.Code	= PTP_OC_NIKON_CurveDownload;
+	ptp.Nparam	= 0;
+	return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size); 
+}
+
+/**
+ * ptp_canon_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;
+	uint16_t	ret;
+	unsigned char	*xdata;
+	unsigned int 	xsize;
+
+	*props = NULL;
+	*size = 0;
+	PTP_CNT_INIT(ptp);
+	ptp.Code	= PTP_OC_NIKON_GetVendorPropCodes;
+	ptp.Nparam	= 0;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &xdata, &xsize); 
+	if (ret == PTP_RC_OK)
+        	*size = ptp_unpack_uint16_t_array(params,xdata,0,props);
+	return ret;
+}
+
+uint16_t
+ptp_nikon_getfileinfoinblock ( PTPParams* params,
+	uint32_t p1, uint32_t p2, uint32_t p3,
+	unsigned char **data, unsigned int *size
+) {
+	PTPContainer ptp;
+	*data = NULL;
+	*size = 0;
+	PTP_CNT_INIT(ptp);
+	ptp.Code	= PTP_OC_NIKON_GetFileInfoInBlock;
+	ptp.Nparam	= 3;
+	ptp.Param1	= p1;
+	ptp.Param2	= p2;
+	ptp.Param3	= 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.Code=PTP_OC_NIKON_GetLiveViewImg;
+        ptp.Nparam=0;
+        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;
+	uint16_t	ret;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_NIKON_GetPreviewImg;
+        ptp.Nparam=0;
+        ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, xdata, xsize);
+	if (ret == PTP_RC_OK) {
+		if (ptp.Nparam > 0)
+			*handle = ptp.Param1;
+	}
+	return ret;
+}
+
+/**
+ * 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;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_CANON_EOS_GetViewFinderData;
+        ptp.Nparam=1;
+        ptp.Param1=0x00100000; /* from trace */
+        return ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, data, size);
+}
+
+/**
+ * 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, int* evtcnt)
+{
+        PTPContainer ptp;
+	uint16_t ret;
+	unsigned char *data = NULL;
+	unsigned int size = 0;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_NIKON_CheckEvent;
+	ptp.Nparam=0;
+	*evtcnt = 0;
+	ret = ptp_transaction (params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	if (ret == PTP_RC_OK) {
+		ptp_unpack_Nikon_EC (params, data, size, event, evtcnt);
+		free (data);
+	}
+	return ret;
+}
+
+/**
+ * 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.Code=PTP_OC_NIKON_GetDevicePTPIPInfo;
+        ptp.Nparam=0;
+        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;
+	unsigned int size;
+	unsigned int pos;
+	unsigned int profn;
+	unsigned int n;
+	char* buffer;
+	uint8_t len;
+	
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_NIKON_GetProfileAllData;
+        ptp.Nparam=0;
+	size = 0;
+	data = NULL;
+	CHECK_PTP_RC(ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size));
+
+	if (size < 2) return PTP_RC_Undefined; /* FIXME: Add more precise error code */
+
+	params->wifi_profiles_version = data[0];
+	params->wifi_profiles_number = data[1];
+	if (params->wifi_profiles)
+		free(params->wifi_profiles);
+	
+	params->wifi_profiles = malloc(params->wifi_profiles_number*sizeof(PTPNIKONWifiProfile));
+	memset(params->wifi_profiles, 0, params->wifi_profiles_number*sizeof(PTPNIKONWifiProfile));
+
+	pos = 2;
+	profn = 0;
+	while (profn < params->wifi_profiles_number && pos < size) {
+		if (pos+6 >= size) return PTP_RC_Undefined;
+		params->wifi_profiles[profn].id = data[pos++];
+		params->wifi_profiles[profn].valid = data[pos++];
+
+		n = dtoh32a(&data[pos]);
+		pos += 4;
+		if (pos+n+4 >= size) return PTP_RC_Undefined;
+		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 = data[pos++];
+		params->wifi_profiles[profn].device_type = data[pos++];
+		params->wifi_profiles[profn].icon_type = data[pos++];
+
+		buffer = ptp_unpack_string(params, data, pos, &len);
+		strncpy(params->wifi_profiles[profn].creation_date, buffer, sizeof(params->wifi_profiles[profn].creation_date));
+		free (buffer);
+		pos += (len*2+1);
+		if (pos+1 >= size) return PTP_RC_Undefined;
+		/* FIXME: check if it is really last usage date */
+		buffer = ptp_unpack_string(params, data, pos, &len);
+		strncpy(params->wifi_profiles[profn].lastusage_date, buffer, sizeof(params->wifi_profiles[profn].lastusage_date));
+		free (buffer);
+		pos += (len*2+1);
+		if (pos+5 >= size) return PTP_RC_Undefined;
+		
+		n = dtoh32a(&data[pos]);
+		pos += 4;
+		if (pos+n >= size) return PTP_RC_Undefined;
+		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
+
+	return PTP_RC_OK;
+}
+
+/**
+ * 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)
+{
+	unsigned char guid[16];
+	
+	PTPContainer ptp;
+	unsigned char buffer[1024];
+	unsigned char* data = buffer;
+	int size = 0;
+	int i;
+	uint8_t len;
+	int profilenr = -1;
+	
+	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 */
+	*((unsigned int*)&buffer[0x3A]) = profile->ip_address; /* Do not reverse bytes */
+	buffer[0x3E] = profile->subnet_mask;
+	*((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.Code=PTP_OC_NIKON_SendProfileData;
+	ptp.Nparam=1;
+	ptp.Param1=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;
+	uint16_t ret;
+	unsigned char *data = NULL;
+	unsigned int size = 0;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_MTP_GetObjectPropsSupported;
+        ptp.Nparam = 1;
+        ptp.Param1 = ofc;
+        ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	if (ret == PTP_RC_OK)
+        	*propnum=ptp_unpack_uint16_t_array(params,data,0,props);
+	free(data);
+	return ret;
+}
+
+/**
+ * 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;
+	uint16_t ret;
+	unsigned char *data = NULL;
+	unsigned int size = 0;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_MTP_GetObjectPropDesc;
+        ptp.Nparam = 2;
+        ptp.Param1 = opc;
+        ptp.Param2 = ofc;
+        ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	if (ret == PTP_RC_OK)
+		ptp_unpack_OPD (params, data, opd, size);
+	free(data);
+	return ret;
+}
+
+/**
+ * ptp_mtp_getobjectpropvalue:
+ *
+ * This command gets the object properties of an object handle.
+ *  
+ * params:	PTPParams*
+ *	uint32_t objectid	- object format code
+ *	uint16_t opc		- object prop code
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+uint16_t
+ptp_mtp_getobjectpropvalue (
+	PTPParams* params, uint32_t oid, uint16_t opc,
+	PTPPropertyValue *value, uint16_t datatype
+) {
+        PTPContainer ptp;
+	uint16_t ret;
+	unsigned char *data = NULL;
+	unsigned int size = 0;
+	int offset = 0;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_MTP_GetObjectPropValue;
+        ptp.Nparam = 2;
+        ptp.Param1 = oid;
+        ptp.Param2 = opc;
+        ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size);
+	if (ret == PTP_RC_OK)
+		ptp_unpack_DPV(params, data, &offset, size, value, datatype);
+	free(data);
+	return ret;
+}
+
+/**
+ * ptp_mtp_setobjectpropvalue:
+ *
+ * This command gets the object properties of an object handle.
+ *  
+ * params:	PTPParams*
+ *	uint32_t objectid	- object format code
+ *	uint16_t opc		- object prop code
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+uint16_t
+ptp_mtp_setobjectpropvalue (
+	PTPParams* params, uint32_t oid, uint16_t opc,
+	PTPPropertyValue *value, uint16_t datatype
+) {
+        PTPContainer ptp;
+	uint16_t ret;
+	unsigned char *data = NULL;
+	unsigned int size ;
+        
+        PTP_CNT_INIT(ptp);
+        ptp.Code=PTP_OC_MTP_SetObjectPropValue;
+        ptp.Nparam = 2;
+        ptp.Param1 = oid;
+        ptp.Param2 = 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* dpv=NULL;
+	unsigned int dpvlen = 0;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code=PTP_OC_MTP_GetObjectReferences;
+	ptp.Param1=handle;
+	ptp.Nparam=1;
+	ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &dpv, &dpvlen);
+	if (ret == PTP_RC_OK) {
+		/* Sandisk Sansa skips the DATA phase, but returns OK as response.
+		 * this will gives us a NULL here. Handle it. -Marcus */
+		if ((dpv == NULL) || (dpvlen == 0)) {
+			*arraylen = 0;
+			*ohArray = NULL;
+		} else {
+			*arraylen = ptp_unpack_uint32_t_array(params, dpv, 0, ohArray);
+		}
+	}
+	free(dpv);
+	return ret;
+}
+
+uint16_t
+ptp_mtp_setobjectreferences (PTPParams* params, uint32_t handle, uint32_t* ohArray, uint32_t arraylen)
+{
+	PTPContainer ptp;
+	uint16_t ret;
+	uint32_t size;
+	unsigned char* dpv=NULL;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code   = PTP_OC_MTP_SetObjectReferences;
+	ptp.Param1 = handle;
+	ptp.Nparam = 1;
+	size = ptp_pack_uint32_t_array(params, ohArray, arraylen, &dpv);
+	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, (unsigned char **)&dpv, NULL);
+	free(dpv);
+	return ret;
+}
+
+uint16_t
+ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPProperties **props, int *nrofprops)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* opldata = NULL;
+	unsigned int oplsize;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code = PTP_OC_MTP_GetObjPropList;
+	ptp.Param1 = handle;
+	ptp.Param2 = 0x00000000U;  /* 0x00000000U should be "all formats" */
+	ptp.Param3 = 0xFFFFFFFFU;  /* 0xFFFFFFFFU should be "all properties" */
+	ptp.Param4 = 0x00000000U;
+	ptp.Param5 = 0x00000000U;
+	ptp.Nparam = 5;
+	ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &opldata, &oplsize);  
+	if (ret == PTP_RC_OK) *nrofprops = ptp_unpack_OPL(params, opldata, props, oplsize);
+	if (opldata != NULL)
+		free(opldata);
+	return ret;
+}
+
+uint16_t
+ptp_mtp_sendobjectproplist (PTPParams* params, uint32_t* store, uint32_t* parenthandle, uint32_t* handle,
+			    uint16_t objecttype, uint64_t objectsize, MTPProperties *props, int nrofprops)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* opldata=NULL;
+	uint32_t oplsize;
+
+	PTP_CNT_INIT(ptp);
+	ptp.Code = PTP_OC_MTP_SendObjectPropList;
+	ptp.Param1 = *store;
+	ptp.Param2 = *parenthandle;
+	ptp.Param3 = (uint32_t) objecttype;
+	ptp.Param4 = (uint32_t) (objectsize >> 32);
+	ptp.Param5 = (uint32_t) (objectsize & 0xffffffffU);
+	ptp.Nparam = 5;
+
+	/* Set object handle to 0 for a new object */
+	oplsize = ptp_pack_OPL(params,props,nrofprops,&opldata);
+	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, oplsize, &opldata, NULL); 
+	free(opldata);
+	*store = ptp.Param1;
+	*parenthandle = ptp.Param2;
+	*handle = ptp.Param3; 
+
+	return ret;
+}
+
+uint16_t
+ptp_mtp_setobjectproplist (PTPParams* params, MTPProperties *props, int nrofprops)
+{
+	uint16_t ret;
+	PTPContainer ptp;
+	unsigned char* opldata=NULL;
+	uint32_t oplsize;
+  
+	PTP_CNT_INIT(ptp);
+	ptp.Code = PTP_OC_MTP_SetObjPropList;
+	ptp.Nparam = 0;
+  
+	oplsize = ptp_pack_OPL(params,props,nrofprops,&opldata);
+	ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, oplsize, &opldata, NULL); 
+	free(opldata);
+
+	return ret;
+}
+
+/* Non PTP protocol functions */
+/* devinfo testing functions */
+
+int
+ptp_operation_issupported(PTPParams* params, uint16_t operation)
+{
+	int i=0;
+
+	for (;i<params->deviceinfo.OperationsSupported_len;i++) {
+		if (params->deviceinfo.OperationsSupported[i]==operation)
+			return 1;
+	}
+	return 0;
+}
+
+
+int
+ptp_event_issupported(PTPParams* params, uint16_t event)
+{
+	int i=0;
+
+	for (;i<params->deviceinfo.EventsSupported_len;i++) {
+		if (params->deviceinfo.EventsSupported[i]==event)
+			return 1;
+	}
+	return 0;
+}
+
+
+int
+ptp_property_issupported(PTPParams* params, uint16_t property)
+{
+	int i;
+
+	for (i=0;i<params->deviceinfo.DevicePropertiesSupported_len;i++)
+		if (params->deviceinfo.DevicePropertiesSupported[i]==property)
+			return 1;
+	return 0;
+}
+
+/* ptp structures freeing functions */
+void
+ptp_free_devicepropvalue(uint16_t dt, PTPPropertyValue* dpd) {
+	switch (dt) {
+	case PTP_DTC_INT8:	case PTP_DTC_UINT8:
+	case PTP_DTC_UINT16:	case PTP_DTC_INT16:
+	case PTP_DTC_UINT32:	case PTP_DTC_INT32:
+	case PTP_DTC_UINT64:	case PTP_DTC_INT64:
+	case PTP_DTC_UINT128:	case PTP_DTC_INT128:
+		/* Nothing to free */
+		break;
+	case PTP_DTC_AINT8:	case PTP_DTC_AUINT8:
+	case PTP_DTC_AUINT16:	case PTP_DTC_AINT16:
+	case PTP_DTC_AUINT32:	case PTP_DTC_AINT32:
+	case PTP_DTC_AUINT64:	case PTP_DTC_AINT64:
+	case PTP_DTC_AUINT128:	case PTP_DTC_AINT128:
+		if (dpd->a.v)
+			free(dpd->a.v);
+		break;
+	case PTP_DTC_STR:
+		if (dpd->str)
+			free(dpd->str);
+		break;
+	}
+}
+
+void
+ptp_free_devicepropdesc(PTPDevicePropDesc* dpd)
+{
+	uint16_t i;
+
+	ptp_free_devicepropvalue (dpd->DataType, &dpd->FactoryDefaultValue);
+	ptp_free_devicepropvalue (dpd->DataType, &dpd->CurrentValue);
+	switch (dpd->FormFlag) {
+	case PTP_DPFF_Range:
+		ptp_free_devicepropvalue (dpd->DataType, &dpd->FORM.Range.MinimumValue);
+		ptp_free_devicepropvalue (dpd->DataType, &dpd->FORM.Range.MaximumValue);
+		ptp_free_devicepropvalue (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_devicepropvalue (dpd->DataType, dpd->FORM.Enum.SupportedValue+i);
+			free (dpd->FORM.Enum.SupportedValue);
+		}
+	}
+}
+
+void
+ptp_free_objectpropdesc(PTPObjectPropDesc* opd)
+{
+	uint16_t i;
+
+	ptp_free_devicepropvalue (opd->DataType, &opd->FactoryDefaultValue);
+	switch (opd->FormFlag) {
+	case PTP_OPFF_None:
+		break;
+	case PTP_OPFF_Range:
+		ptp_free_devicepropvalue (opd->DataType, &opd->FORM.Range.MinimumValue);
+		ptp_free_devicepropvalue (opd->DataType, &opd->FORM.Range.MaximumValue);
+		ptp_free_devicepropvalue (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_devicepropvalue (opd->DataType, opd->FORM.Enum.SupportedValue+i);
+			free (opd->FORM.Enum.SupportedValue);
+		}
+		break;
+	case PTP_OPFF_DateTime:
+	case PTP_OPFF_FixedLengthArray:
+	case PTP_OPFF_RegularExpression:
+	case PTP_OPFF_ByteArray:
+	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_objectinfo (PTPObjectInfo *oi)
+{
+	if (!oi) return;
+        free (oi->Filename); oi->Filename = NULL;
+        free (oi->Keywords); oi->Keywords = NULL;
+}
+
+void
+ptp_free_object (PTPObject *ob)
+{
+	int i;
+	if (!ob) return;
+
+	ptp_free_objectinfo (&ob->oi);
+	for (i=0;i<ob->nrofmtpprops;i++)
+		ptp_destroy_object_prop(&ob->mtpprops[i]);
+	ob->flags = 0;
+}
+
+void 
+ptp_perror(PTPParams* params, uint16_t error) {
+
+	int i;
+	/* PTP error descriptions */
+	static struct {
+		short n;
+		const char *txt;
+	} ptp_errors[] = {
+	{PTP_RC_Undefined, 		N_("PTP: Undefined Error")},
+	{PTP_RC_OK, 			N_("PTP: OK!")},
+	{PTP_RC_GeneralError, 		N_("PTP: General Error")},
+	{PTP_RC_SessionNotOpen, 	N_("PTP: Session Not Open")},
+	{PTP_RC_InvalidTransactionID, 	N_("PTP: Invalid Transaction ID")},
+	{PTP_RC_OperationNotSupported, 	N_("PTP: Operation Not Supported")},
+	{PTP_RC_ParameterNotSupported, 	N_("PTP: Parameter Not Supported")},
+	{PTP_RC_IncompleteTransfer, 	N_("PTP: Incomplete Transfer")},
+	{PTP_RC_InvalidStorageId, 	N_("PTP: Invalid Storage ID")},
+	{PTP_RC_InvalidObjectHandle, 	N_("PTP: Invalid Object Handle")},
+	{PTP_RC_DevicePropNotSupported, N_("PTP: Device Prop Not Supported")},
+	{PTP_RC_InvalidObjectFormatCode, N_("PTP: Invalid Object Format Code")},
+	{PTP_RC_StoreFull, 		N_("PTP: Store Full")},
+	{PTP_RC_ObjectWriteProtected, 	N_("PTP: Object Write Protected")},
+	{PTP_RC_StoreReadOnly, 		N_("PTP: Store Read Only")},
+	{PTP_RC_AccessDenied,		N_("PTP: Access Denied")},
+	{PTP_RC_NoThumbnailPresent, 	N_("PTP: No Thumbnail Present")},
+	{PTP_RC_SelfTestFailed, 	N_("PTP: Self Test Failed")},
+	{PTP_RC_PartialDeletion, 	N_("PTP: Partial Deletion")},
+	{PTP_RC_StoreNotAvailable, 	N_("PTP: Store Not Available")},
+	{PTP_RC_SpecificationByFormatUnsupported,
+				N_("PTP: Specification By Format Unsupported")},
+	{PTP_RC_NoValidObjectInfo, 	N_("PTP: No Valid Object Info")},
+	{PTP_RC_InvalidCodeFormat, 	N_("PTP: Invalid Code Format")},
+	{PTP_RC_UnknownVendorCode, 	N_("PTP: Unknown Vendor Code")},
+	{PTP_RC_CaptureAlreadyTerminated,
+					N_("PTP: Capture Already Terminated")},
+	{PTP_RC_DeviceBusy, 		N_("PTP: Device Busy")},
+	{PTP_RC_InvalidParentObject, 	N_("PTP: Invalid Parent Object")},
+	{PTP_RC_InvalidDevicePropFormat, N_("PTP: Invalid Device Prop Format")},
+	{PTP_RC_InvalidDevicePropValue, N_("PTP: Invalid Device Prop Value")},
+	{PTP_RC_InvalidParameter, 	N_("PTP: Invalid Parameter")},
+	{PTP_RC_SessionAlreadyOpened, 	N_("PTP: Session Already Opened")},
+	{PTP_RC_TransactionCanceled, 	N_("PTP: Transaction Canceled")},
+	{PTP_RC_SpecificationOfDestinationUnsupported,
+			N_("PTP: Specification Of Destination Unsupported")},
+	{PTP_RC_EK_FilenameRequired,	N_("PTP: EK Filename Required")},
+	{PTP_RC_EK_FilenameConflicts,	N_("PTP: EK Filename Conflicts")},
+	{PTP_RC_EK_FilenameInvalid,	N_("PTP: EK Filename Invalid")},
+
+	{PTP_ERROR_IO,		  N_("PTP: I/O error")},
+	{PTP_ERROR_BADPARAM,	  N_("PTP: Error: bad parameter")},
+	{PTP_ERROR_DATA_EXPECTED, N_("PTP: Protocol error, data expected")},
+	{PTP_ERROR_RESP_EXPECTED, N_("PTP: Protocol error, response expected")},
+	{0, NULL}
+};
+
+	for (i=0; ptp_errors[i].txt!=NULL; i++)
+		if (ptp_errors[i].n == error)
+			ptp_error(params, ptp_errors[i].txt);
+}
+
+const char*
+ptp_get_property_description(PTPParams* params, uint16_t dpc)
+{
+	int i;
+	/* Device Property descriptions */
+	struct {
+		uint16_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")},
+		{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")},
+		{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_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_LiveViewAF,			/* 0xD05D */
+		 N_("Live View AF")},
+		{PTP_DPC_NIKON_AELockMode,			/* 0xD05E */
+		 N_("Exposure Lock")},
+		{PTP_DPC_NIKON_AELAFLMode,			/* 0xD05F */
+		 N_("Focus Lock")},
+		{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_AngleLevel,			/* 0xD067 */
+		 N_("Angle Level")},
+		{PTP_DPC_NIKON_D1ShootingSpeed,			/* 0xD068 */
+		 N_("Shooting Speed")},
+		{PTP_DPC_NIKON_D2MaximumShots,			/* 0xD069 */
+		 N_("Maximum Shots")},
+		{PTP_DPC_NIKON_D3ExpDelayMode,			/* 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_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_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_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_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_ISOAutoTime,			/* 0xD14E */
+		 N_("ISO Auto Time")},
+		{PTP_DPC_NIKON_FlourescentType,			/* 0xD14F */
+		 N_("Flourescent 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_ISOAuto,				/* 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_ExposureIndicateLightup,		/* 0xD1B2 */
+		 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")},
+		{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}
+        };
+
+	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)
+		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);
+
+	return NULL;
+}
+
+static int64_t
+_value_to_num(PTPPropertyValue *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, int length, char *out)
+{
+	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_DigitalZoom, 0, 0.1, 0.0, "%.1f"},		/* 5016 */
+
+		/* 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. */
+		{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_("Default")},
+		{PTP_DPC_FlashMode, PTP_VENDOR_NIKON, 32785, N_("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")},
+
+
+		/* 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_D3ExpDelayMode,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_BOOL(PTP_DPC_NIKON_MovVoice,PTP_VENDOR_NIKON),		/* D0A1 */
+
+		PTP_VENDOR_VAL_BOOL(PTP_DPC_NIKON_Bracketing,PTP_VENDOR_NIKON),		/* D0C0 */
+
+		{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_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_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_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 */
+
+		/* 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) {
+		switch (dpc) {
+		case PTP_DPC_MTP_SynchronizationPartner:
+		case PTP_DPC_MTP_DeviceFriendlyName:
+			return snprintf(out, length, "%s", dpd->CurrentValue.str);
+		case PTP_DPC_MTP_SecureTime:
+		case PTP_DPC_MTP_DeviceCertificate: {
+			/* 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_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)
+{
+	int i;
+	
+	if (!(ofc & 0x8000)) {
+		for (i=0;i<sizeof(ptp_ofc_trans)/sizeof(ptp_ofc_trans[0]);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_MICROSOFT:
+		case PTP_VENDOR_MTP:		  
+			for (i=0;i<sizeof(ptp_ofc_mtp_trans)/sizeof(ptp_ofc_mtp_trans[0]);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);
+}
+
+struct {
+	uint16_t opcode;
+	const char *name;
+} 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")}
+};
+
+struct {
+	uint16_t opcode;
+	const char *name;
+} 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")}
+};
+
+int
+ptp_render_opcode(PTPParams* params, uint16_t opcode, int spaceleft, char *txt)
+{
+	int i;
+
+	if (!(opcode & 0x8000)) {
+		for (i=0;i<sizeof(ptp_opcode_trans)/sizeof(ptp_opcode_trans[0]);i++)
+			if (opcode == ptp_opcode_trans[i].opcode)
+				return snprintf(txt, spaceleft, "%s", _(ptp_opcode_trans[i].name));
+	} else {
+		switch (params->deviceinfo.VendorExtensionID) {
+		case PTP_VENDOR_MICROSOFT:
+		case PTP_VENDOR_MTP:
+			for (i=0;i<sizeof(ptp_opcode_mtp_trans)/sizeof(ptp_opcode_mtp_trans[0]);i++)
+				if (opcode == ptp_opcode_mtp_trans[i].opcode)
+					return snprintf(txt, spaceleft, "%s", _(ptp_opcode_mtp_trans[i].name));
+			break;
+		default:break;
+		}
+	}
+	return snprintf (txt, spaceleft,_("Unknown (%04x)"), opcode);
+}
+
+
+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) {
+	int i;
+	for (i=0;i<sizeof(ptp_opc_trans)/sizeof(ptp_opc_trans[0]);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);
+}
+
+/*
+ * Allocate and default-initialize a few object properties.
+ */
+MTPProperties *
+ptp_get_new_object_prop_entry(MTPProperties **props, int *nrofprops) {
+	MTPProperties *newprops;
+	MTPProperties *prop;
+
+	if (*props == NULL) {
+		newprops = malloc(sizeof(MTPProperties)*(*nrofprops+1));
+	} else {
+		newprops = realloc(*props,sizeof(MTPProperties)*(*nrofprops+1));
+	}
+	if (newprops == NULL)
+		return NULL;
+	prop = &newprops[*nrofprops];
+	prop->property = PTP_OPC_StorageID; /* Should be "unknown" */
+	prop->datatype = PTP_DTC_UNDEF;
+	prop->ObjectHandle = 0x00000000U;
+	prop->propval.str = NULL;
+	
+	(*props) = newprops;
+	(*nrofprops)++;
+	return prop;
+}
+
+void 
+ptp_destroy_object_prop(MTPProperties *prop)
+{
+  if (!prop)
+    return;
+  
+  if (prop->datatype == PTP_DTC_STR && prop->propval.str != NULL)
+    free(prop->propval.str);
+  else if ((prop->datatype == PTP_DTC_AINT8 || prop->datatype == PTP_DTC_AINT16 ||
+            prop->datatype == PTP_DTC_AINT32 || prop->datatype == PTP_DTC_AINT64 || prop->datatype == PTP_DTC_AINT128 ||
+            prop->datatype == PTP_DTC_AUINT8 || prop->datatype == PTP_DTC_AUINT16 ||
+            prop->datatype == PTP_DTC_AUINT32 || prop->datatype == PTP_DTC_AUINT64 || prop->datatype ==  PTP_DTC_AUINT128)
+            && prop->propval.a.v != NULL)
+    free(prop->propval.a.v);
+}
+
+void 
+ptp_destroy_object_prop_list(MTPProperties *props, int nrofprops)
+{
+  int i;
+  MTPProperties *prop = props;
+
+  for (i=0;i<nrofprops;i++,prop++)
+    ptp_destroy_object_prop(prop);
+  free(props);
+}
+
+/*
+ * Find a certain object property in the cache, i.e. a certain metadata
+ * item for a certain object handle.
+ */
+MTPProperties *
+ptp_find_object_prop_in_cache(PTPParams *params, uint32_t const handle, uint32_t const attribute_id)
+{
+	int	i;
+	MTPProperties	*prop;
+	PTPObject	*ob;
+	uint16_t	ret;
+
+	ret = ptp_object_find (params, handle, &ob);
+	if (ret != PTP_RC_OK)
+		return NULL;
+	prop = ob->mtpprops;
+	for (i=0;i<ob->nrofmtpprops;i++) {
+		if (attribute_id == prop->property)
+			return prop;
+		prop++;
+	}
+	return NULL;
+}
+
+void
+ptp_remove_object_from_cache(PTPParams *params, uint32_t handle)
+{
+	int i;
+	PTPObject	*ob;
+	uint16_t	ret;
+
+	ret = ptp_object_find (params, handle, &ob);
+	if (ret != PTP_RC_OK)
+		return;
+	i = ob-params->objects;
+	/* remove object from object info cache */
+	ptp_free_object (ob);
+
+	if (i < params->nrofobjects-1)
+		memmove (ob,ob+1,(params->nrofobjects-1-i)*sizeof(PTPObject));
+	params->nrofobjects--;
+	/* We use less memory than before so this shouldn't fail */
+	params->objects = realloc(params->objects, sizeof(PTPObject)*params->nrofobjects);
+}
+
+static int _cmp_ob (const void *a, const void *b) {
+	PTPObject *oa = (PTPObject*)a;
+	PTPObject *ob = (PTPObject*)b;
+
+	return oa->oid - ob->oid;
+}
+	
+void
+ptp_objects_sort (PTPParams *params) {
+	qsort (params->objects, params->nrofobjects, sizeof(PTPObject), _cmp_ob);
+}
+
+/* Binary search in objects. Needs "objects" to be a sorted by objectid list!  */
+uint16_t
+ptp_object_find (PTPParams *params, uint32_t handle, PTPObject **retob) {
+	PTPObject	tmpob;
+
+	tmpob.oid = handle;
+	*retob = bsearch (&tmpob, params->objects, params->nrofobjects, sizeof(tmpob), _cmp_ob);
+	if (!*retob)
+		return PTP_RC_GeneralError;
+	return PTP_RC_OK;
+}
+
+/* Binary search in objects + insert of not found. Needs "objects" to be a sorted by objectid list!  */
+uint16_t
+ptp_object_find_or_insert (PTPParams *params, uint32_t handle, PTPObject **retob) {
+	int 		begin, end, cursor;
+	int		insertat;
+	PTPObject	*newobs;
+
+	if (!handle) return PTP_RC_GeneralError;
+	*retob = NULL;
+	if (!params->nrofobjects) {
+		params->objects = calloc(1,sizeof(PTPObject));
+		params->nrofobjects = 1;
+		params->objects[0].oid = handle;
+		*retob = &params->objects[0];
+		return PTP_RC_OK;
+	}
+	begin = 0;
+	end = params->nrofobjects-1;
+	/*ptp_debug (params, "searching %08x, total=%d", handle, params->nrofobjects);*/
+	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[cursor].oid == handle) {
+			*retob = &params->objects[cursor];
+			return PTP_RC_OK;
+		}
+		if (params->objects[cursor].oid < handle)
+			begin = cursor;
+		else
+			end = cursor;
+		if ((end - begin) <= 1)
+			break;
+	}
+	if (params->objects[begin].oid == handle) {
+		*retob = &params->objects[begin];
+		return PTP_RC_OK;
+	}
+	if (params->objects[end].oid == handle) {
+		*retob = &params->objects[end];
+		return PTP_RC_OK;
+	}
+	if ((begin == 0) && (handle < params->objects[0].oid)) {
+		insertat=begin;
+	} else {
+		if ((end == params->nrofobjects-1) && (handle > params->objects[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);*/
+	newobs = realloc (params->objects, sizeof(PTPObject)*(params->nrofobjects+1));
+	if (!newobs) return PTP_RC_GeneralError;
+	params->objects = newobs;
+	if (insertat<=params->nrofobjects)
+		memmove (&params->objects[insertat+1],&params->objects[insertat],(params->nrofobjects-insertat)*sizeof(PTPObject));
+	memset(&params->objects[insertat],0,sizeof(PTPObject));
+	params->objects[insertat].oid = handle;
+	*retob = &params->objects[insertat];
+	params->nrofobjects++;
+	return PTP_RC_OK;
+}
+
+uint16_t
+ptp_object_want (PTPParams *params, uint32_t handle, int want, PTPObject **retob) {
+	uint16_t	ret;
+	PTPObject	*ob;
+	//Camera 		*camera = ((PTPData *)params->data)->camera;
+
+	*retob = NULL;
+	if (!handle) {
+		ptp_debug (params, "ptp_object_want: querying handle 0?\n");
+		return PTP_RC_GeneralError;
+	}
+	ret = ptp_object_find_or_insert (params, handle, &ob);
+	if (ret != PTP_RC_OK)
+		return PTP_RC_GeneralError;
+	*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)
+			return ret;
+		if (!ob->oi.Filename) ob->oi.Filename=strdup("<none>");
+		if (ob->flags & PTPOBJECT_PARENTOBJECT_LOADED)
+			ob->oi.ParentObject = saveparent;
+
+		/* Second EOS issue, 0x20000000 has 0x20000000 as parent */
+		if (ob->oi.ParentObject == handle)
+			ob->oi.ParentObject = 0;
+		ob->flags |= X;
+
+		/* EOS bug, DCIM links back to itself. */
+	}
+#undef X
+	if (	(want & PTPOBJECT_MTPPROPLIST_LOADED) &&
+		(!(ob->flags & PTPOBJECT_MTPPROPLIST_LOADED))
+	) {
+		int		nrofprops = 0;
+		MTPProperties 	*props = NULL;
+
+		if (params->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST) {
+			want &= ~PTPOBJECT_MTPPROPLIST_LOADED;
+			goto fallback;
+		}
+
+		/* Microsoft/MTP has fast directory retrieval. */
+		if (!ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList)) {
+			want &= ~PTPOBJECT_MTPPROPLIST_LOADED;
+			goto fallback;
+		}
+
+		ptp_debug (params, "ptp2/mtpfast: reading mtp proplist of %08x", handle);
+		ret = ptp_mtp_getobjectproplist (params, handle, &props, &nrofprops);
+		if (ret != PTP_RC_OK)
+			goto fallback;
+		ob->mtpprops = props;
+		ob->nrofmtpprops = nrofprops;
+
+#if 0
+		MTPProperties 	*xpl;
+		int j;
+		PTPObjectInfo	oinfo;	
+
+		memset (&oinfo,0,sizeof(oinfo));
+		/* hmm, not necessary ... only if we would use it */
+		for (j=0;j<nrofprops;j++) {
+			xpl = &props[j];
+			switch (xpl->property) {
+			case PTP_OPC_ParentObject:
+				if (xpl->datatype != PTP_DTC_UINT32) {
+					ptp_debug (params, "ptp2/mtpfast: parentobject has type 0x%x???", xpl->datatype);
+					break;
+				}
+				oinfo.ParentObject = xpl->propval.u32;
+				ptp_debug (params, "ptp2/mtpfast: parent 0x%x", xpl->propval.u32);
+				break;
+			case PTP_OPC_ObjectFormat:
+				if (xpl->datatype != PTP_DTC_UINT16) {
+					ptp_debug (params, "ptp2/mtpfast: objectformat has type 0x%x???", xpl->datatype);
+					break;
+				}
+				oinfo.ObjectFormat = xpl->propval.u16;
+				ptp_debug (params, "ptp2/mtpfast: ofc 0x%x", xpl->propval.u16);
+				break;
+			case PTP_OPC_ObjectSize:
+				switch (xpl->datatype) {
+				case PTP_DTC_UINT32:
+					oinfo.ObjectCompressedSize = xpl->propval.u32;
+					break;
+				case PTP_DTC_UINT64:
+					oinfo.ObjectCompressedSize = xpl->propval.u64;
+					break;
+				default:
+					ptp_debug (params, "ptp2/mtpfast: objectsize has type 0x%x???", xpl->datatype);
+					break;
+				}
+				ptp_debug (params, "ptp2/mtpfast: objectsize %u", xpl->propval.u32);
+				break;
+			case PTP_OPC_StorageID:
+				if (xpl->datatype != PTP_DTC_UINT32) {
+					ptp_debug (params, "ptp2/mtpfast: storageid has type 0x%x???", xpl->datatype);
+					break;
+				}
+				oinfo.StorageID = xpl->propval.u32;
+				ptp_debug (params, "ptp2/mtpfast: storageid 0x%x", xpl->propval.u32);
+				break;
+			case PTP_OPC_ProtectionStatus:/*UINT16*/
+				if (xpl->datatype != PTP_DTC_UINT16) {
+					ptp_debug (params, "ptp2/mtpfast: protectionstatus has type 0x%x???", xpl->datatype);
+					break;
+				}
+				oinfo.ProtectionStatus = xpl->propval.u16;
+				ptp_debug (params, "ptp2/mtpfast: protection 0x%x", xpl->propval.u16);
+				break;
+			case PTP_OPC_ObjectFileName:
+				if (xpl->datatype != PTP_DTC_STR) {
+					ptp_debug (params, "ptp2/mtpfast: filename has type 0x%x???", xpl->datatype);
+					break;
+				}
+				if (xpl->propval.str) {
+					ptp_debug (params, "ptp2/mtpfast: filename %s", xpl->propval.str);
+					oinfo.Filename = strdup(xpl->propval.str);
+				} else {
+					oinfo.Filename = NULL;
+				}
+				break;
+			case PTP_OPC_DateCreated:
+				if (xpl->datatype != PTP_DTC_STR) {
+					ptp_debug (params, "ptp2/mtpfast: datecreated has type 0x%x???", xpl->datatype);
+					break;
+				}
+				ptp_debug (params, "ptp2/mtpfast: capturedate %s", xpl->propval.str);
+				oinfo.CaptureDate = ptp_unpack_PTPTIME (xpl->propval.str);
+				break;
+			case PTP_OPC_DateModified:
+				if (xpl->datatype != PTP_DTC_STR) {
+					ptp_debug (params, "ptp2/mtpfast: datemodified has type 0x%x???", xpl->datatype);
+					break;
+				}
+				ptp_debug (params, "ptp2/mtpfast: moddate %s", xpl->propval.str);
+				oinfo.ModificationDate = ptp_unpack_PTPTIME (xpl->propval.str);
+				break;
+			default:
+				if ((xpl->property & 0xfff0) == 0xdc00)
+					ptp_debug (params, "ptp2/mtpfast:case %x type %x unhandled", xpl->property, xpl->datatype);
+				break;
+			}
+		}
+		if (!oinfo.Filename)
+			/* i have one such file on my Creative */
+			oinfo.Filename = strdup("<null>");
+#endif
+		ob->flags |= PTPOBJECT_MTPPROPLIST_LOADED;
+fallback:	;
+	}
+	if ((ob->flags & want) == want)
+		return PTP_RC_OK;
+	ptp_debug (params, "ptp_object_want: oid 0x%08x, want flags %x, have only %x?", handle, want, ob->flags);
+	return PTP_RC_GeneralError;
+}
+
+
+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);
+}
diff --git a/src/ptp.h b/src/ptp.h
new file mode 100644
index 0000000..a558ff5
--- /dev/null
+++ b/src/ptp.h
@@ -0,0 +1,2572 @@
+/* ptp.h
+ *
+ * Copyright (C) 2001 Mariusz Woloszyn <emsi@ipartners.pl>
+ * Copyright (C) 2003-2009 Marcus Meissner <marcus@jet.franken.de>
+ * Copyright (C) 2006-2008 Linus Walleij <triad@df.lth.se>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PTP_H__
+#define __PTP_H__
+
+#include <stdarg.h>
+#include <time.h>
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+#include "gphoto2-endian.h"
+#include "device-flags.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* PTP datalayer byteorder */
+
+#define PTP_DL_BE			0xF0
+#define	PTP_DL_LE			0x0F
+
+/* USB interface class */
+#ifndef USB_CLASS_PTP
+#define USB_CLASS_PTP			6
+#endif
+
+/* PTP request/response/event general PTP container (transport independent) */
+
+struct _PTPContainer {
+	uint16_t Code;
+	uint32_t SessionID;
+	uint32_t Transaction_ID;
+	/* params  may be of any type of size less or equal to uint32_t */
+	uint32_t Param1;
+	uint32_t Param2;
+	uint32_t Param3;
+	/* events can only have three parameters */
+	uint32_t Param4;
+	uint32_t Param5;
+	/* the number of meaningfull parameters */
+	uint8_t	 Nparam;
+};
+typedef struct _PTPContainer PTPContainer;
+
+/* PTP USB Bulk-Pipe container */
+/* USB bulk max packet length for high speed endpoints */
+/* The max packet is set to 512 bytes. The spec says
+ * "end of data transfers are signaled by short packets or NULL
+ * packets". It never says anything about 512, but current
+ * implementations seem to have chosen this value, which also
+ * happens to be the size of an USB 2.0 HS endpoint, even though
+ * this is not necessary.
+ *
+ * Previously we had this as 4096 for MTP devices. We have found
+ * and fixed the bugs that made this necessary and it can be 512 again.
+ */
+#define PTP_USB_BULK_HS_MAX_PACKET_LEN_WRITE	512
+#define PTP_USB_BULK_HS_MAX_PACKET_LEN_READ   512
+#define PTP_USB_BULK_HDR_LEN		(2*sizeof(uint32_t)+2*sizeof(uint16_t))
+#define PTP_USB_BULK_PAYLOAD_LEN_WRITE	(PTP_USB_BULK_HS_MAX_PACKET_LEN_WRITE-PTP_USB_BULK_HDR_LEN)
+#define PTP_USB_BULK_PAYLOAD_LEN_READ	(PTP_USB_BULK_HS_MAX_PACKET_LEN_READ-PTP_USB_BULK_HDR_LEN)
+#define PTP_USB_BULK_REQ_LEN	(PTP_USB_BULK_HDR_LEN+5*sizeof(uint32_t))
+
+struct _PTPUSBBulkContainer {
+	uint32_t length;
+	uint16_t type;
+	uint16_t code;
+	uint32_t trans_id;
+	union {
+		struct {
+			uint32_t param1;
+			uint32_t param2;
+			uint32_t param3;
+			uint32_t param4;
+			uint32_t param5;
+		} params;
+       /* this must be set to the maximum of PTP_USB_BULK_PAYLOAD_LEN_WRITE 
+        * and PTP_USB_BULK_PAYLOAD_LEN_READ */
+		unsigned char data[PTP_USB_BULK_PAYLOAD_LEN_READ];
+	} payload;
+};
+typedef struct _PTPUSBBulkContainer PTPUSBBulkContainer;
+
+/* PTP USB Asynchronous Event Interrupt Data Format */
+struct _PTPUSBEventContainer {
+	uint32_t length;
+	uint16_t type;
+	uint16_t code;
+	uint32_t trans_id;
+	uint32_t param1;
+	uint32_t param2;
+	uint32_t param3;
+};
+typedef struct _PTPUSBEventContainer PTPUSBEventContainer;
+
+struct _PTPCanon_directtransfer_entry {
+	uint32_t	oid;
+	char		*str;
+};
+typedef struct _PTPCanon_directtransfer_entry PTPCanon_directtransfer_entry;
+
+/* USB container types */
+
+#define PTP_USB_CONTAINER_UNDEFINED		0x0000
+#define PTP_USB_CONTAINER_COMMAND		0x0001
+#define PTP_USB_CONTAINER_DATA			0x0002
+#define PTP_USB_CONTAINER_RESPONSE		0x0003
+#define PTP_USB_CONTAINER_EVENT			0x0004
+
+/* PTP/IP definitions */
+#define PTPIP_INIT_COMMAND_REQUEST	1
+#define PTPIP_INIT_COMMAND_ACK		2
+#define PTPIP_INIT_EVENT_REQUEST	3
+#define PTPIP_INIT_EVENT_ACK		4
+#define PTPIP_INIT_FAIL			5
+#define PTPIP_CMD_REQUEST		6
+#define PTPIP_CMD_RESPONSE		7
+#define PTPIP_EVENT			8
+#define PTPIP_START_DATA_PACKET		9
+#define PTPIP_DATA_PACKET		10
+#define PTPIP_CANCEL_TRANSACTION	11
+#define PTPIP_END_DATA_PACKET		12
+#define PTPIP_PING			13
+#define PTPIP_PONG			14
+
+struct _PTPIPHeader {
+	uint32_t	length;
+	uint32_t	type;
+};
+typedef struct _PTPIPHeader PTPIPHeader;
+
+/* Vendor IDs */
+#define PTP_VENDOR_EASTMAN_KODAK	0x00000001
+#define PTP_VENDOR_SEIKO_EPSON		0x00000002
+#define PTP_VENDOR_AGILENT		0x00000003
+#define PTP_VENDOR_POLAROID		0x00000004
+#define PTP_VENDOR_AGFA_GEVAERT		0x00000005
+#define PTP_VENDOR_MICROSOFT		0x00000006
+#define PTP_VENDOR_EQUINOX		0x00000007
+#define PTP_VENDOR_VIEWQUEST		0x00000008
+#define PTP_VENDOR_STMICROELECTRONICS	0x00000009
+#define PTP_VENDOR_NIKON		0x0000000A
+#define PTP_VENDOR_CANON		0x0000000B
+  
+/* Vendor extension ID used for MTP */
+#define PTP_VENDOR_MTP			0xffffffff  
+
+/* Operation Codes */
+
+/* PTP v1.0 operation codes */
+#define PTP_OC_Undefined                0x1000
+#define PTP_OC_GetDeviceInfo            0x1001
+#define PTP_OC_OpenSession              0x1002
+#define PTP_OC_CloseSession             0x1003
+#define PTP_OC_GetStorageIDs            0x1004
+#define PTP_OC_GetStorageInfo           0x1005
+#define PTP_OC_GetNumObjects            0x1006
+#define PTP_OC_GetObjectHandles         0x1007
+#define PTP_OC_GetObjectInfo            0x1008
+#define PTP_OC_GetObject                0x1009
+#define PTP_OC_GetThumb                 0x100A
+#define PTP_OC_DeleteObject             0x100B
+#define PTP_OC_SendObjectInfo           0x100C
+#define PTP_OC_SendObject               0x100D
+#define PTP_OC_InitiateCapture          0x100E
+#define PTP_OC_FormatStore              0x100F
+#define PTP_OC_ResetDevice              0x1010
+#define PTP_OC_SelfTest                 0x1011
+#define PTP_OC_SetObjectProtection      0x1012
+#define PTP_OC_PowerDown                0x1013
+#define PTP_OC_GetDevicePropDesc        0x1014
+#define PTP_OC_GetDevicePropValue       0x1015
+#define PTP_OC_SetDevicePropValue       0x1016
+#define PTP_OC_ResetDevicePropValue     0x1017
+#define PTP_OC_TerminateOpenCapture     0x1018
+#define PTP_OC_MoveObject               0x1019
+#define PTP_OC_CopyObject               0x101A
+#define PTP_OC_GetPartialObject         0x101B
+#define PTP_OC_InitiateOpenCapture      0x101C
+/* PTP v1.1 operation codes */
+#define PTP_OC_StartEnumHandles		0x101D
+#define PTP_OC_EnumHandles		0x101E
+#define PTP_OC_StopEnumHandles		0x101F
+#define PTP_OC_GetVendorExtensionMaps	0x1020
+#define PTP_OC_GetVendorDeviceInfo	0x1021
+#define PTP_OC_GetResizedImageObject	0x1022
+#define PTP_OC_GetFilesystemManifest	0x1023
+#define PTP_OC_GetStreamInfo		0x1024
+#define PTP_OC_GetStream		0x1025
+
+/* Eastman Kodak extension Operation Codes */
+#define PTP_OC_EK_GetSerial		0x9003
+#define PTP_OC_EK_SetSerial		0x9004
+#define PTP_OC_EK_SendFileObjectInfo	0x9005
+#define PTP_OC_EK_SendFileObject	0x9006
+#define PTP_OC_EK_SetText		0x9008
+
+/* Canon extension Operation Codes */
+#define PTP_OC_CANON_GetPartialObjectInfo	0x9001
+/* 9002 - sends 2 uint32, nothing back  */
+#define PTP_OC_CANON_SetObjectArchive		0x9002
+#define PTP_OC_CANON_KeepDeviceOn		0x9003
+#define PTP_OC_CANON_LockDeviceUI		0x9004
+#define PTP_OC_CANON_UnlockDeviceUI		0x9005
+#define PTP_OC_CANON_GetObjectHandleByName	0x9006
+/* no 9007 observed yet */
+#define PTP_OC_CANON_InitiateReleaseControl	0x9008
+#define PTP_OC_CANON_TerminateReleaseControl	0x9009
+#define PTP_OC_CANON_TerminatePlaybackMode	0x900A
+#define PTP_OC_CANON_ViewfinderOn		0x900B
+#define PTP_OC_CANON_ViewfinderOff		0x900C
+#define PTP_OC_CANON_DoAeAfAwb			0x900D
+
+/* 900e - send nothing, gets 5 uint16t in 32bit entities back in 20byte datablob */
+#define PTP_OC_CANON_GetCustomizeSpec		0x900E
+#define PTP_OC_CANON_GetCustomizeItemInfo	0x900F
+#define PTP_OC_CANON_GetCustomizeData		0x9010
+#define PTP_OC_CANON_SetCustomizeData		0x9011
+#define PTP_OC_CANON_GetCaptureStatus		0x9012
+#define PTP_OC_CANON_CheckEvent			0x9013
+#define PTP_OC_CANON_FocusLock			0x9014
+#define PTP_OC_CANON_FocusUnlock		0x9015
+#define PTP_OC_CANON_GetLocalReleaseParam	0x9016
+#define PTP_OC_CANON_SetLocalReleaseParam	0x9017
+#define PTP_OC_CANON_AskAboutPcEvf		0x9018
+#define PTP_OC_CANON_SendPartialObject		0x9019
+#define PTP_OC_CANON_InitiateCaptureInMemory	0x901A
+#define PTP_OC_CANON_GetPartialObjectEx		0x901B
+#define PTP_OC_CANON_SetObjectTime		0x901C
+#define PTP_OC_CANON_GetViewfinderImage		0x901D
+#define PTP_OC_CANON_GetObjectAttributes	0x901E
+#define PTP_OC_CANON_ChangeUSBProtocol		0x901F
+#define PTP_OC_CANON_GetChanges			0x9020
+#define PTP_OC_CANON_GetObjectInfoEx		0x9021
+#define PTP_OC_CANON_InitiateDirectTransfer	0x9022
+#define PTP_OC_CANON_TerminateDirectTransfer 	0x9023
+#define PTP_OC_CANON_SendObjectInfoByPath 	0x9024
+#define PTP_OC_CANON_SendObjectByPath 		0x9025
+#define PTP_OC_CANON_InitiateDirectTansferEx	0x9026
+#define PTP_OC_CANON_GetAncillaryObjectHandles	0x9027
+#define PTP_OC_CANON_GetTreeInfo 		0x9028
+#define PTP_OC_CANON_GetTreeSize 		0x9029
+#define PTP_OC_CANON_NotifyProgress 		0x902A
+#define PTP_OC_CANON_NotifyCancelAccepted	0x902B
+/* 902c: no parms, read 3 uint32 in data, no response parms */
+#define PTP_OC_CANON_902C			0x902C
+#define PTP_OC_CANON_GetDirectory		0x902D
+
+#define PTP_OC_CANON_SetPairingInfo		0x9030
+#define PTP_OC_CANON_GetPairingInfo		0x9031
+#define PTP_OC_CANON_DeletePairingInfo		0x9032
+#define PTP_OC_CANON_GetMACAddress		0x9033
+/* 9034: 1 param, no parms returned */
+#define PTP_OC_CANON_SetDisplayMonitor		0x9034
+#define PTP_OC_CANON_PairingComplete		0x9035
+#define PTP_OC_CANON_GetWirelessMAXChannel	0x9036
+
+/* 9101: no args, 8 byte data (01 00 00 00 00 00 00 00), no resp data. */
+#define PTP_OC_CANON_EOS_GetStorageIDs		0x9101
+/* 9102: 1 arg (0)
+ * 0x28 bytes of data:
+    00000000: 34 00 00 00 02 00 02 91 0a 00 00 00 04 00 03 00
+    00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+    00000020: 00 00 ff ff ff ff 03 43 00 46 00 00 00 03 41 00
+    00000030: 3a 00 00 00
+ * no resp args
+ */
+#define PTP_OC_CANON_EOS_GetStorageInfo		0x9102
+#define PTP_OC_CANON_EOS_GetObjectInfo		0x9103
+#define PTP_OC_CANON_EOS_GetObject		0x9104
+#define PTP_OC_CANON_EOS_DeleteObject		0x9105
+#define PTP_OC_CANON_EOS_FormatStore		0x9106
+#define PTP_OC_CANON_EOS_GetPartialObject	0x9107
+#define PTP_OC_CANON_EOS_GetDeviceInfoEx	0x9108
+
+/* sample1:
+ * 3 cmdargs: 1,0xffffffff,00 00 10 00;
+ * data:
+    00000000: 48 00 00 00 02 00 09 91 12 00 00 00 01 00 00 00
+    00000010: 38 00 00 00 00 00 00 30 01 00 00 00 01 30 00 00
+    00000020: 01 00 00 00 10 00 00 00 00 00 00 00 00 00 00 20
+    00000030: 00 00 00 30 44 43 49 4d 00 00 00 00 00 00 00 00	DCIM
+    00000040: 00 00 00 00 cc c3 01 46
+ * 2 respargs: 0x0, 0x3c
+ * 
+ * sample2:
+ *
+    00000000: 18 00 00 00 01 00 09 91 15 00 00 00 01 00 00 00
+    00000010: 00 00 00 30 00 00 10 00
+
+    00000000: 48 00 00 00 02 00 09 91 15 00 00 00 01 00 00 00
+    00000010: 38 00 00 00 00 00 9c 33 01 00 00 00 01 30 00 00
+    00000020: 01 00 00 00 10 00 00 00 00 00 00 00 00 00 00 30
+    00000030: 00 00 9c 33 32 33 31 43 41 4e 4f 4e 00 00 00 00	 231CANON
+    00000040: 00 00 00 00 cc c3 01 46
+
+ */
+#define PTP_OC_CANON_EOS_GetObjectInfoEx	0x9109
+#define PTP_OC_CANON_EOS_GetThumbEx		0x910A
+#define PTP_OC_CANON_EOS_SendPartialObject	0x910B
+#define PTP_OC_CANON_EOS_SetObjectAttributes	0x910C
+#define PTP_OC_CANON_EOS_GetObjectTime		0x910D
+#define PTP_OC_CANON_EOS_SetObjectTime		0x910E
+
+/* 910f: no args, no data, 1 response arg (0). */
+#define PTP_OC_CANON_EOS_RemoteRelease		0x910F
+/* Marcus: looks more like "Set DeviceProperty" in the trace. 
+ *
+ * no cmd args
+ * data phase (0xc, 0xd11c, 0x1)
+ * no resp args 
+ */
+#define PTP_OC_CANON_EOS_SetDevicePropValueEx	0x9110
+#define PTP_OC_CANON_EOS_GetRemoteMode		0x9113
+/* 9114: 1 arg (0x1), no data, no resp data. */
+#define PTP_OC_CANON_EOS_SetRemoteMode		0x9114
+/* 9115: 1 arg (0x1), no data, no resp data. */
+#define PTP_OC_CANON_EOS_SetEventMode		0x9115
+/* 9116: no args, data phase, no resp data. */
+#define PTP_OC_CANON_EOS_GetEvent		0x9116
+#define PTP_OC_CANON_EOS_TransferComplete	0x9117
+#define PTP_OC_CANON_EOS_CancelTransfer		0x9118
+#define PTP_OC_CANON_EOS_ResetTransfer		0x9119
+
+/* 911a: 3 args (0xfffffff7, 0x00001000, 0x00000001), no data, no resp data. */
+/* 911a: 3 args (0x001dfc60, 0x00001000, 0x00000001), no data, no resp data. */
+#define PTP_OC_CANON_EOS_PCHDDCapacity		0x911A
+
+/* 911b: no cmd args, no data, no resp args */
+#define PTP_OC_CANON_EOS_SetUILock		0x911B
+/* 911c: no cmd args, no data, no resp args */
+#define PTP_OC_CANON_EOS_ResetUILock		0x911C
+#define PTP_OC_CANON_EOS_KeepDeviceOn		0x911D
+#define PTP_OC_CANON_EOS_SetNullPacketMode	0x911E
+#define PTP_OC_CANON_EOS_UpdateFirmware		0x911F
+#define PTP_OC_CANON_EOS_TransferCompleteDT	0x9120
+#define PTP_OC_CANON_EOS_CancelTransferDT	0x9121
+#define PTP_OC_CANON_EOS_SetWftProfile		0x9122
+#define PTP_OC_CANON_EOS_GetWftProfile		0x9122
+#define PTP_OC_CANON_EOS_SetProfileToWft	0x9124
+#define PTP_OC_CANON_EOS_BulbStart		0x9125
+#define PTP_OC_CANON_EOS_BulbEnd		0x9126
+#define PTP_OC_CANON_EOS_RequestDevicePropValue	0x9127
+
+/* 0x9128 args (0x1/0x2, 0x0), no data, no resp args */
+#define PTP_OC_CANON_EOS_RemoteReleaseOn	0x9128
+/* 0x9129 args (0x1/0x2), no data, no resp args */
+#define PTP_OC_CANON_EOS_RemoteReleaseOff	0x9129
+
+#define PTP_OC_CANON_EOS_InitiateViewfinder	0x9151
+#define PTP_OC_CANON_EOS_TerminateViewfinder	0x9152
+#define PTP_OC_CANON_EOS_GetViewFinderData	0x9153
+#define PTP_OC_CANON_EOS_DoAf			0x9154
+#define PTP_OC_CANON_EOS_DriveLens		0x9155
+#define PTP_OC_CANON_EOS_DepthOfFieldPreview	0x9156
+#define PTP_OC_CANON_EOS_ClickWB		0x9157
+#define PTP_OC_CANON_EOS_Zoom			0x9158
+#define PTP_OC_CANON_EOS_ZoomPosition		0x9159
+#define PTP_OC_CANON_EOS_SetLiveAfFrame		0x915a
+#define PTP_OC_CANON_EOS_AfCancel		0x9160
+#define PTP_OC_CANON_EOS_FAPIMessageTX		0x91FE
+#define PTP_OC_CANON_EOS_FAPIMessageRX		0x91FF
+
+/* Nikon extension Operation Codes */
+#define PTP_OC_NIKON_GetProfileAllData	0x9006
+#define PTP_OC_NIKON_SendProfileData	0x9007
+#define PTP_OC_NIKON_DeleteProfile	0x9008
+#define PTP_OC_NIKON_SetProfileData	0x9009
+#define PTP_OC_NIKON_AdvancedTransfer	0x9010
+#define PTP_OC_NIKON_GetFileInfoInBlock	0x9011
+#define PTP_OC_NIKON_Capture		0x90C0	/* 1 param,   no data */
+#define PTP_OC_NIKON_AfDrive		0x90C1	/* no params, no data */
+#define PTP_OC_NIKON_SetControlMode	0x90C2	/* 1 param,   no data */
+#define PTP_OC_NIKON_DelImageSDRAM	0x90C3	/* no params, no data */
+#define PTP_OC_NIKON_GetLargeThumb	0x90C4
+#define PTP_OC_NIKON_CurveDownload	0x90C5	/* 1 param,   data in */
+#define PTP_OC_NIKON_CurveUpload	0x90C6	/* 1 param,   data out */
+#define PTP_OC_NIKON_CheckEvent		0x90C7	/* no params, data in */
+#define PTP_OC_NIKON_DeviceReady	0x90C8	/* no params, no data */
+#define PTP_OC_NIKON_SetPreWBData	0x90C9	/* 3 params,  data out */
+#define PTP_OC_NIKON_GetVendorPropCodes	0x90CA	/* 0 params, data in */
+#define PTP_OC_NIKON_AfCaptureSDRAM	0x90CB	/* no params, no data */
+#define PTP_OC_NIKON_GetPictCtrlData	0x90CC
+#define PTP_OC_NIKON_SetPictCtrlData	0x90CD
+#define PTP_OC_NIKON_DelCstPicCtrl	0x90CE
+#define PTP_OC_NIKON_GetPicCtrlCapability	0x90CF
+
+/* Nikon Liveview stuff */
+#define PTP_OC_NIKON_GetPreviewImg	0x9200
+#define PTP_OC_NIKON_StartLiveView	0x9201
+#define PTP_OC_NIKON_EndLiveView	0x9202
+#define PTP_OC_NIKON_GetLiveViewImg	0x9203
+#define PTP_OC_NIKON_MfDrive		0x9204
+#define PTP_OC_NIKON_ChangeAfArea	0x9205
+#define PTP_OC_NIKON_AfDriveCancel	0x9206
+
+#define PTP_OC_NIKON_GetDevicePTPIPInfo	0x90E0
+
+/* Microsoft / MTP extension codes */
+
+#define PTP_OC_MTP_GetObjectPropsSupported	0x9801
+#define PTP_OC_MTP_GetObjectPropDesc		0x9802
+#define PTP_OC_MTP_GetObjectPropValue		0x9803
+#define PTP_OC_MTP_SetObjectPropValue		0x9804
+#define PTP_OC_MTP_GetObjPropList		0x9805
+#define PTP_OC_MTP_SetObjPropList		0x9806
+#define PTP_OC_MTP_GetInterdependendPropdesc	0x9807
+#define PTP_OC_MTP_SendObjectPropList		0x9808
+#define PTP_OC_MTP_GetObjectReferences		0x9810
+#define PTP_OC_MTP_SetObjectReferences		0x9811
+#define PTP_OC_MTP_UpdateDeviceFirmware		0x9812
+#define PTP_OC_MTP_Skip				0x9820
+
+/*
+ * Windows Media Digital Rights Management for Portable Devices 
+ * Extension Codes (microsoft.com/WMDRMPD: 10.1)
+ */
+#define PTP_OC_MTP_WMDRMPD_GetSecureTimeChallenge	0x9101
+#define PTP_OC_MTP_WMDRMPD_GetSecureTimeResponse	0x9102
+#define PTP_OC_MTP_WMDRMPD_SetLicenseResponse	0x9103
+#define PTP_OC_MTP_WMDRMPD_GetSyncList		0x9104
+#define PTP_OC_MTP_WMDRMPD_SendMeterChallengeQuery	0x9105
+#define PTP_OC_MTP_WMDRMPD_GetMeterChallenge	0x9106
+#define PTP_OC_MTP_WMDRMPD_SetMeterResponse		0x9107
+#define PTP_OC_MTP_WMDRMPD_CleanDataStore		0x9108
+#define PTP_OC_MTP_WMDRMPD_GetLicenseState		0x9109
+#define PTP_OC_MTP_WMDRMPD_SendWMDRMPDCommand	0x910A
+#define PTP_OC_MTP_WMDRMPD_SendWMDRMPDRequest	0x910B
+
+/* 
+ * Windows Media Digital Rights Management for Portable Devices 
+ * Extension Codes (microsoft.com/WMDRMPD: 10.1)
+ * Below are operations that have no public documented identifier 
+ * associated with them "Vendor-defined Command Code"
+ */
+#define PTP_OC_MTP_WMDRMPD_SendWMDRMPDAppRequest	0x9212
+#define PTP_OC_MTP_WMDRMPD_GetWMDRMPDAppResponse	0x9213
+#define PTP_OC_MTP_WMDRMPD_EnableTrustedFilesOperations	0x9214
+#define PTP_OC_MTP_WMDRMPD_DisableTrustedFilesOperations 0x9215
+#define PTP_OC_MTP_WMDRMPD_EndTrustedAppSession		0x9216
+/* ^^^ guess ^^^ */
+
+/*
+ * Microsoft Advanced Audio/Video Transfer 
+ * Extensions (microsoft.com/AAVT: 1.0)
+ */
+#define PTP_OC_MTP_AAVT_OpenMediaSession		0x9170
+#define PTP_OC_MTP_AAVT_CloseMediaSession		0x9171
+#define PTP_OC_MTP_AAVT_GetNextDataBlock		0x9172
+#define PTP_OC_MTP_AAVT_SetCurrentTimePosition		0x9173
+
+/*
+ * Windows Media Digital Rights Management for Network Devices 
+ * Extensions (microsoft.com/WMDRMND: 1.0) MTP/IP?
+ */
+#define PTP_OC_MTP_WMDRMND_SendRegistrationRequest	0x9180
+#define PTP_OC_MTP_WMDRMND_GetRegistrationResponse	0x9181
+#define PTP_OC_MTP_WMDRMND_GetProximityChallenge	0x9182
+#define PTP_OC_MTP_WMDRMND_SendProximityResponse	0x9183
+#define PTP_OC_MTP_WMDRMND_SendWMDRMNDLicenseRequest	0x9184
+#define PTP_OC_MTP_WMDRMND_GetWMDRMNDLicenseResponse	0x9185
+
+/* 
+ * Windows Media Player Portiable Devices 
+ * Extension Codes (microsoft.com/WMPPD: 11.1)
+ */
+#define PTP_OC_MTP_WMPPD_ReportAddedDeletedItems	0x9201
+#define PTP_OC_MTP_WMPPD_ReportAcquiredItems 	        0x9202
+#define PTP_OC_MTP_WMPPD_PlaylistObjectPref		0x9203
+
+/*
+ * Undocumented Zune Operation Codes 
+ * maybe related to WMPPD extension set?
+ */
+#define PTP_OC_MTP_ZUNE_GETUNDEFINED001		        0x9204
+
+/* WiFi Provisioning MTP Extension Codes (microsoft.com/WPDWCN: 1.0) */
+#define PTP_OC_MTP_WPDWCN_ProcessWFCObject		0x9122
+
+/* Proprietary vendor extension operations mask */
+#define PTP_OC_EXTENSION_MASK           0xF000
+#define PTP_OC_EXTENSION                0x9000
+
+/* Response Codes */
+
+/* PTP v1.0 response codes */
+#define PTP_RC_Undefined                0x2000
+#define PTP_RC_OK                       0x2001
+#define PTP_RC_GeneralError             0x2002
+#define PTP_RC_SessionNotOpen           0x2003
+#define PTP_RC_InvalidTransactionID	0x2004
+#define PTP_RC_OperationNotSupported    0x2005
+#define PTP_RC_ParameterNotSupported    0x2006
+#define PTP_RC_IncompleteTransfer       0x2007
+#define PTP_RC_InvalidStorageId         0x2008
+#define PTP_RC_InvalidObjectHandle      0x2009
+#define PTP_RC_DevicePropNotSupported   0x200A
+#define PTP_RC_InvalidObjectFormatCode  0x200B
+#define PTP_RC_StoreFull                0x200C
+#define PTP_RC_ObjectWriteProtected     0x200D
+#define PTP_RC_StoreReadOnly            0x200E
+#define PTP_RC_AccessDenied             0x200F
+#define PTP_RC_NoThumbnailPresent       0x2010
+#define PTP_RC_SelfTestFailed           0x2011
+#define PTP_RC_PartialDeletion          0x2012
+#define PTP_RC_StoreNotAvailable        0x2013
+#define PTP_RC_SpecificationByFormatUnsupported         0x2014
+#define PTP_RC_NoValidObjectInfo        0x2015
+#define PTP_RC_InvalidCodeFormat        0x2016
+#define PTP_RC_UnknownVendorCode        0x2017
+#define PTP_RC_CaptureAlreadyTerminated 0x2018
+#define PTP_RC_DeviceBusy               0x2019
+#define PTP_RC_InvalidParentObject      0x201A
+#define PTP_RC_InvalidDevicePropFormat  0x201B
+#define PTP_RC_InvalidDevicePropValue   0x201C
+#define PTP_RC_InvalidParameter         0x201D
+#define PTP_RC_SessionAlreadyOpened     0x201E
+#define PTP_RC_TransactionCanceled      0x201F
+#define PTP_RC_SpecificationOfDestinationUnsupported            0x2020
+/* PTP v1.1 response codes */
+#define PTP_RC_InvalidEnumHandle	0x2021
+#define PTP_RC_NoStreamEnabled		0x2022
+#define PTP_RC_InvalidDataSet		0x2023
+
+/* Eastman Kodak extension Response Codes */
+#define PTP_RC_EK_FilenameRequired	0xA001
+#define PTP_RC_EK_FilenameConflicts	0xA002
+#define PTP_RC_EK_FilenameInvalid	0xA003
+
+/* Nikon specific response codes */
+#define PTP_RC_NIKON_HardwareError		0xA001
+#define PTP_RC_NIKON_OutOfFocus			0xA002
+#define PTP_RC_NIKON_ChangeCameraModeFailed	0xA003
+#define PTP_RC_NIKON_InvalidStatus		0xA004
+#define PTP_RC_NIKON_SetPropertyNotSupported	0xA005
+#define PTP_RC_NIKON_WbResetError		0xA006
+#define PTP_RC_NIKON_DustReferenceError		0xA007
+#define PTP_RC_NIKON_ShutterSpeedBulb		0xA008
+#define PTP_RC_NIKON_MirrorUpSequence		0xA009
+#define PTP_RC_NIKON_CameraModeNotAdjustFNumber	0xA00A
+#define PTP_RC_NIKON_NotLiveView		0xA00B
+#define PTP_RC_NIKON_MfDriveStepEnd		0xA00C
+#define PTP_RC_NIKON_MfDriveStepInsufficiency	0xA00E
+#define PTP_RC_NIKON_AdvancedTransferCancel	0xA022
+
+/* Canon specific response codes */
+#define PTP_RC_CANON_A009		0xA009
+
+/* Microsoft/MTP specific codes */
+#define PTP_RC_MTP_Undefined			0xA800
+#define PTP_RC_MTP_Invalid_ObjectPropCode	0xA801
+#define PTP_RC_MTP_Invalid_ObjectProp_Format	0xA802
+#define PTP_RC_MTP_Invalid_ObjectProp_Value	0xA803
+#define PTP_RC_MTP_Invalid_ObjectReference	0xA804
+#define PTP_RC_MTP_Invalid_Dataset		0xA806
+#define PTP_RC_MTP_Specification_By_Group_Unsupported		0xA807
+#define PTP_RC_MTP_Specification_By_Depth_Unsupported		0xA808
+#define PTP_RC_MTP_Object_Too_Large		0xA809
+#define PTP_RC_MTP_ObjectProp_Not_Supported	0xA80A
+
+/* Microsoft Advanced Audio/Video Transfer response codes 
+(microsoft.com/AAVT 1.0) */
+#define PTP_RC_MTP_Invalid_Media_Session_ID	0xA170	
+#define PTP_RC_MTP_Media_Session_Limit_Reached	0xA171
+#define PTP_RC_MTP_No_More_Data			0xA172
+
+/* WiFi Provisioning MTP Extension Error Codes (microsoft.com/WPDWCN: 1.0) */
+#define PTP_RC_MTP_Invalid_WFC_Syntax		0xA121
+#define PTP_RC_MTP_WFC_Version_Not_Supported	0xA122
+
+/* libptp2 extended ERROR codes */
+#define PTP_ERROR_IO			0x02FF
+#define PTP_ERROR_DATA_EXPECTED		0x02FE
+#define PTP_ERROR_RESP_EXPECTED		0x02FD
+#define PTP_ERROR_BADPARAM		0x02FC
+#define PTP_ERROR_CANCEL		0x02FB
+#define PTP_ERROR_TIMEOUT		0x02FA
+
+/* PTP Event Codes */
+
+#define PTP_EC_Undefined		0x4000
+#define PTP_EC_CancelTransaction	0x4001
+#define PTP_EC_ObjectAdded		0x4002
+#define PTP_EC_ObjectRemoved		0x4003
+#define PTP_EC_StoreAdded		0x4004
+#define PTP_EC_StoreRemoved		0x4005
+#define PTP_EC_DevicePropChanged	0x4006
+#define PTP_EC_ObjectInfoChanged	0x4007
+#define PTP_EC_DeviceInfoChanged	0x4008
+#define PTP_EC_RequestObjectTransfer	0x4009
+#define PTP_EC_StoreFull		0x400A
+#define PTP_EC_DeviceReset		0x400B
+#define PTP_EC_StorageInfoChanged	0x400C
+#define PTP_EC_CaptureComplete		0x400D
+#define PTP_EC_UnreportedStatus		0x400E
+
+/* Canon extension Event Codes */
+#define PTP_EC_CANON_ExtendedErrorcode		0xC005	/* ? */
+#define PTP_EC_CANON_ObjectInfoChanged		0xC008
+#define PTP_EC_CANON_RequestObjectTransfer	0xC009
+#define PTP_EC_CANON_CameraModeChanged		0xC00C
+#define PTP_EC_CANON_ShutterButtonPressed	0xC00E
+
+#define PTP_EC_CANON_StartDirectTransfer	0xC011
+#define PTP_EC_CANON_StopDirectTransfer		0xC013
+
+/* Canon EOS events */
+#define PTP_EC_CANON_EOS_RequestGetEvent	0xc101
+#define PTP_EC_CANON_EOS_ObjectAddedEx		0xc181
+#define PTP_EC_CANON_EOS_ObjectRemoved		0xc182
+#define PTP_EC_CANON_EOS_RequestGetObjectInfoEx	0xc183
+#define PTP_EC_CANON_EOS_StorageStatusChanged	0xc184
+#define PTP_EC_CANON_EOS_StorageInfoChanged	0xc185
+#define PTP_EC_CANON_EOS_RequestObjectTransfer	0xc186
+#define PTP_EC_CANON_EOS_ObjectInfoChangedEx	0xc187
+#define PTP_EC_CANON_EOS_ObjectContentChanged	0xc188
+#define PTP_EC_CANON_EOS_PropValueChanged	0xc189
+#define PTP_EC_CANON_EOS_AvailListChanged	0xc18a
+#define PTP_EC_CANON_EOS_CameraStatusChanged	0xc18b
+#define PTP_EC_CANON_EOS_WillSoonShutdown	0xc18d
+#define PTP_EC_CANON_EOS_ShutdownTimerUpdated	0xc18e
+#define PTP_EC_CANON_EOS_RequestCancelTransfer	0xc18f
+#define PTP_EC_CANON_EOS_RequestObjectTransferDT	0xc190
+#define PTP_EC_CANON_EOS_RequestCancelTransferDT	0xc191
+#define PTP_EC_CANON_EOS_StoreAdded		0xc192
+#define PTP_EC_CANON_EOS_StoreRemoved		0xc193
+#define PTP_EC_CANON_EOS_BulbExposureTime	0xc194
+#define PTP_EC_CANON_EOS_RecordingTime		0xc195
+#define PTP_EC_CANON_EOS_RequestObjectTransferTS		0xC1a2
+#define PTP_EC_CANON_EOS_AfResult		0xc1a3
+
+/* Nikon extension Event Codes */
+
+/* Nikon extension Event Codes */
+#define PTP_EC_Nikon_ObjectAddedInSDRAM		0xC101
+#define PTP_EC_Nikon_CaptureCompleteRecInSdram	0xC102
+/* Gets 1 parameter, objectid pointing to DPOF object */
+#define PTP_EC_Nikon_AdvancedTransfer		0xC103
+#define PTP_EC_Nikon_PreviewImageAdded		0xC104
+
+/* MTP Event codes */
+#define PTP_EC_MTP_ObjectPropChanged		0xC801
+#define PTP_EC_MTP_ObjectPropDescChanged	0xC802
+#define PTP_EC_MTP_ObjectReferencesChanged	0xC803
+
+/* constants for GetObjectHandles */
+#define PTP_GOH_ALL_STORAGE 0xffffffff
+#define PTP_GOH_ALL_FORMATS 0x00000000
+#define PTP_GOH_ALL_ASSOCS  0x00000000
+#define PTP_GOH_ROOT_PARENT 0xffffffff
+
+/* PTP device info structure (returned by GetDevInfo) */
+
+struct _PTPDeviceInfo {
+	uint16_t StandardVersion;
+	uint32_t VendorExtensionID;
+	uint16_t VendorExtensionVersion;
+	char	*VendorExtensionDesc;
+	uint16_t FunctionalMode;
+	uint32_t OperationsSupported_len;
+	uint16_t *OperationsSupported;
+	uint32_t EventsSupported_len;
+	uint16_t *EventsSupported;
+	uint32_t DevicePropertiesSupported_len;
+	uint16_t *DevicePropertiesSupported;
+	uint32_t CaptureFormats_len;
+	uint16_t *CaptureFormats;
+	uint32_t ImageFormats_len;
+	uint16_t *ImageFormats;
+	char	*Manufacturer;
+	char	*Model;
+	char	*DeviceVersion;
+	char	*SerialNumber;
+};
+typedef struct _PTPDeviceInfo PTPDeviceInfo;
+
+/* PTP storageIDs structute (returned by GetStorageIDs) */
+
+struct _PTPStorageIDs {
+	uint32_t n;
+	uint32_t *Storage;
+};
+typedef struct _PTPStorageIDs PTPStorageIDs;
+
+/* PTP StorageInfo structure (returned by GetStorageInfo) */
+struct _PTPStorageInfo {
+	uint16_t StorageType;
+	uint16_t FilesystemType;
+	uint16_t AccessCapability;
+	uint64_t MaxCapability;
+	uint64_t FreeSpaceInBytes;
+	uint32_t FreeSpaceInImages;
+	char 	*StorageDescription;
+	char	*VolumeLabel;
+};
+typedef struct _PTPStorageInfo PTPStorageInfo;
+
+/* PTP objecthandles structure (returned by GetObjectHandles) */
+
+struct _PTPObjectHandles {
+	uint32_t n;
+	uint32_t *Handler;
+};
+typedef struct _PTPObjectHandles PTPObjectHandles;
+
+#define PTP_HANDLER_SPECIAL	0xffffffff
+#define PTP_HANDLER_ROOT	0x00000000
+
+
+/* PTP objectinfo structure (returned by GetObjectInfo) */
+
+struct _PTPObjectInfo {
+	uint32_t StorageID;
+	uint16_t ObjectFormat;
+	uint16_t ProtectionStatus;
+	uint32_t ObjectCompressedSize;
+	uint16_t ThumbFormat;
+	uint32_t ThumbCompressedSize;
+	uint32_t ThumbPixWidth;
+	uint32_t ThumbPixHeight;
+	uint32_t ImagePixWidth;
+	uint32_t ImagePixHeight;
+	uint32_t ImageBitDepth;
+	uint32_t ParentObject;
+	uint16_t AssociationType;
+	uint32_t AssociationDesc;
+	uint32_t SequenceNumber;
+	char 	*Filename;
+	time_t	CaptureDate;
+	time_t	ModificationDate;
+	char	*Keywords;
+};
+typedef struct _PTPObjectInfo PTPObjectInfo;
+
+/* max ptp string length INCLUDING terminating null character */
+
+#define PTP_MAXSTRLEN				255
+
+/* PTP Object Format Codes */
+
+/* ancillary formats */
+#define PTP_OFC_Undefined			0x3000
+#define PTP_OFC_Defined				0x3800
+#define PTP_OFC_Association			0x3001
+#define PTP_OFC_Script				0x3002
+#define PTP_OFC_Executable			0x3003
+#define PTP_OFC_Text				0x3004
+#define PTP_OFC_HTML				0x3005
+#define PTP_OFC_DPOF				0x3006
+#define PTP_OFC_AIFF	 			0x3007
+#define PTP_OFC_WAV				0x3008
+#define PTP_OFC_MP3				0x3009
+#define PTP_OFC_AVI				0x300A
+#define PTP_OFC_MPEG				0x300B
+#define PTP_OFC_ASF				0x300C
+#define PTP_OFC_QT				0x300D /* guessing */
+/* image formats */
+#define PTP_OFC_EXIF_JPEG			0x3801
+#define PTP_OFC_TIFF_EP				0x3802
+#define PTP_OFC_FlashPix			0x3803
+#define PTP_OFC_BMP				0x3804
+#define PTP_OFC_CIFF				0x3805
+#define PTP_OFC_Undefined_0x3806		0x3806
+#define PTP_OFC_GIF				0x3807
+#define PTP_OFC_JFIF				0x3808
+#define PTP_OFC_PCD				0x3809
+#define PTP_OFC_PICT				0x380A
+#define PTP_OFC_PNG				0x380B
+#define PTP_OFC_Undefined_0x380C		0x380C
+#define PTP_OFC_TIFF				0x380D
+#define PTP_OFC_TIFF_IT				0x380E
+#define PTP_OFC_JP2				0x380F
+#define PTP_OFC_JPX				0x3810
+/* ptp v1.1 has only DNG new */
+#define PTP_OFC_DNG				0x3811
+/* Eastman Kodak extension ancillary format */
+#define PTP_OFC_EK_M3U				0xb002
+/* Canon extension */
+#define PTP_OFC_CANON_CRW			0xb101
+#define PTP_OFC_CANON_CRW3			0xb103
+#define PTP_OFC_CANON_MOV			0xb104
+/* MTP extensions */
+#define PTP_OFC_MTP_MediaCard			0xb211
+#define PTP_OFC_MTP_MediaCardGroup		0xb212
+#define PTP_OFC_MTP_Encounter			0xb213
+#define PTP_OFC_MTP_EncounterBox		0xb214
+#define PTP_OFC_MTP_M4A				0xb215
+#define PTP_OFC_MTP_ZUNEUNDEFINED		0xb217 /* Unknown file type */
+#define PTP_OFC_MTP_Firmware			0xb802
+#define PTP_OFC_MTP_WindowsImageFormat		0xb881
+#define PTP_OFC_MTP_UndefinedAudio		0xb900
+#define PTP_OFC_MTP_WMA				0xb901
+#define PTP_OFC_MTP_OGG				0xb902
+#define PTP_OFC_MTP_AAC				0xb903
+#define PTP_OFC_MTP_AudibleCodec		0xb904
+#define PTP_OFC_MTP_FLAC			0xb906
+#define PTP_OFC_MTP_UndefinedVideo		0xb980
+#define PTP_OFC_MTP_WMV				0xb981
+#define PTP_OFC_MTP_MP4				0xb982
+#define PTP_OFC_MTP_MP2				0xb983
+#define PTP_OFC_MTP_3GP				0xb984
+#define PTP_OFC_MTP_UndefinedCollection		0xba00
+#define PTP_OFC_MTP_AbstractMultimediaAlbum	0xba01
+#define PTP_OFC_MTP_AbstractImageAlbum		0xba02
+#define PTP_OFC_MTP_AbstractAudioAlbum		0xba03
+#define PTP_OFC_MTP_AbstractVideoAlbum		0xba04
+#define PTP_OFC_MTP_AbstractAudioVideoPlaylist	0xba05
+#define PTP_OFC_MTP_AbstractContactGroup	0xba06
+#define PTP_OFC_MTP_AbstractMessageFolder	0xba07
+#define PTP_OFC_MTP_AbstractChapteredProduction	0xba08
+#define PTP_OFC_MTP_AbstractAudioPlaylist	0xba09
+#define PTP_OFC_MTP_AbstractVideoPlaylist	0xba0a
+#define PTP_OFC_MTP_AbstractMediacast		0xba0b
+#define PTP_OFC_MTP_WPLPlaylist			0xba10
+#define PTP_OFC_MTP_M3UPlaylist			0xba11
+#define PTP_OFC_MTP_MPLPlaylist			0xba12
+#define PTP_OFC_MTP_ASXPlaylist			0xba13
+#define PTP_OFC_MTP_PLSPlaylist			0xba14
+#define PTP_OFC_MTP_UndefinedDocument		0xba80
+#define PTP_OFC_MTP_AbstractDocument		0xba81
+#define PTP_OFC_MTP_XMLDocument			0xba82
+#define PTP_OFC_MTP_MSWordDocument		0xba83
+#define PTP_OFC_MTP_MHTCompiledHTMLDocument	0xba84
+#define PTP_OFC_MTP_MSExcelSpreadsheetXLS	0xba85
+#define PTP_OFC_MTP_MSPowerpointPresentationPPT	0xba86
+#define PTP_OFC_MTP_UndefinedMessage		0xbb00
+#define PTP_OFC_MTP_AbstractMessage		0xbb01
+#define PTP_OFC_MTP_UndefinedContact		0xbb80
+#define PTP_OFC_MTP_AbstractContact		0xbb81
+#define PTP_OFC_MTP_vCard2			0xbb82
+#define PTP_OFC_MTP_vCard3			0xbb83
+#define PTP_OFC_MTP_UndefinedCalendarItem	0xbe00
+#define PTP_OFC_MTP_AbstractCalendarItem	0xbe01
+#define PTP_OFC_MTP_vCalendar1			0xbe02
+#define PTP_OFC_MTP_vCalendar2			0xbe03
+#define PTP_OFC_MTP_UndefinedWindowsExecutable	0xbe80
+#define PTP_OFC_MTP_MediaCast			0xbe81
+#define PTP_OFC_MTP_Section			0xbe82
+
+/* PTP Association Types */
+#define PTP_AT_Undefined			0x0000
+#define PTP_AT_GenericFolder			0x0001
+#define PTP_AT_Album				0x0002
+#define PTP_AT_TimeSequence			0x0003
+#define PTP_AT_HorizontalPanoramic		0x0004
+#define PTP_AT_VerticalPanoramic		0x0005
+#define PTP_AT_2DPanoramic			0x0006
+#define PTP_AT_AncillaryData			0x0007
+
+/* PTP Protection Status */
+
+#define PTP_PS_NoProtection			0x0000
+#define PTP_PS_ReadOnly				0x0001
+#define PTP_PS_MTP_ReadOnlyData			0x8002
+#define PTP_PS_MTP_NonTransferableData		0x8003
+
+/* PTP Storage Types */
+
+#define PTP_ST_Undefined			0x0000
+#define PTP_ST_FixedROM				0x0001
+#define PTP_ST_RemovableROM			0x0002
+#define PTP_ST_FixedRAM				0x0003
+#define PTP_ST_RemovableRAM			0x0004
+
+/* PTP FilesystemType Values */
+
+#define PTP_FST_Undefined			0x0000
+#define PTP_FST_GenericFlat			0x0001
+#define PTP_FST_GenericHierarchical		0x0002
+#define PTP_FST_DCF				0x0003
+
+/* PTP StorageInfo AccessCapability Values */
+
+#define PTP_AC_ReadWrite			0x0000
+#define PTP_AC_ReadOnly				0x0001
+#define PTP_AC_ReadOnly_with_Object_Deletion	0x0002
+
+/* Property Describing Dataset, Range Form */
+
+union _PTPPropertyValue {
+	char		*str;	/* common string, malloced */
+	uint8_t		u8;
+	int8_t		i8;
+	uint16_t	u16;
+	int16_t		i16;
+	uint32_t	u32;
+	int32_t		i32;
+	uint64_t	u64;
+	int64_t		i64;
+	/* XXXX: 128 bit signed and unsigned missing */
+	struct array {
+		uint32_t	count;
+		union _PTPPropertyValue	*v;	/* malloced, count elements */
+	} a;
+};
+
+typedef union _PTPPropertyValue PTPPropertyValue;
+
+/* Metadata lists for MTP operations */
+struct _MTPProperties {
+	uint16_t 	 	property;
+	uint16_t 	 	datatype;
+	uint32_t 	 	ObjectHandle;
+	PTPPropertyValue 	propval;
+};
+typedef struct _MTPProperties MTPProperties;
+
+struct _PTPPropDescRangeForm {
+	PTPPropertyValue 	MinimumValue;
+	PTPPropertyValue 	MaximumValue;
+	PTPPropertyValue 	StepSize;
+};
+typedef struct _PTPPropDescRangeForm PTPPropDescRangeForm;
+
+/* Property Describing Dataset, Enum Form */
+
+struct _PTPPropDescEnumForm {
+	uint16_t		NumberOfValues;
+	PTPPropertyValue	*SupportedValue;	/* malloced */
+};
+typedef struct _PTPPropDescEnumForm PTPPropDescEnumForm;
+
+/* Device Property Describing Dataset (DevicePropDesc) */
+
+struct _PTPDevicePropDesc {
+	uint16_t		DevicePropertyCode;
+	uint16_t		DataType;
+	uint8_t			GetSet;
+	PTPPropertyValue	FactoryDefaultValue;
+	PTPPropertyValue	CurrentValue;
+	uint8_t			FormFlag;
+	union	{
+		PTPPropDescEnumForm	Enum;
+		PTPPropDescRangeForm	Range;
+	} FORM;
+};
+typedef struct _PTPDevicePropDesc PTPDevicePropDesc;
+
+/* Object Property Describing Dataset (DevicePropDesc) */
+
+struct _PTPObjectPropDesc {
+	uint16_t		ObjectPropertyCode;
+	uint16_t		DataType;
+	uint8_t			GetSet;
+	PTPPropertyValue	FactoryDefaultValue;
+	uint32_t		GroupCode;
+	uint8_t			FormFlag;
+	union	{
+		PTPPropDescEnumForm	Enum;
+		PTPPropDescRangeForm	Range;
+	} FORM;
+};
+typedef struct _PTPObjectPropDesc PTPObjectPropDesc;
+
+/* Canon filesystem's folder entry Dataset */
+
+#define PTP_CANON_FilenameBufferLen	13
+#define PTP_CANON_FolderEntryLen	28
+
+struct _PTPCANONFolderEntry {
+	uint32_t	ObjectHandle;
+	uint16_t	ObjectFormatCode;
+	uint8_t		Flags;
+	uint32_t	ObjectSize;
+	time_t		Time;
+	char		Filename[PTP_CANON_FilenameBufferLen];
+};
+typedef struct _PTPCANONFolderEntry PTPCANONFolderEntry;
+
+/* Nikon Tone Curve Data */
+
+#define PTP_NIKON_MaxCurvePoints 19
+
+struct _PTPNIKONCoordinatePair {
+	uint8_t		X;
+	uint8_t		Y;
+};
+
+typedef struct _PTPNIKONCoordinatePair PTPNIKONCoordinatePair;
+
+struct _PTPNTCCoordinatePair {
+	uint8_t		X;
+	uint8_t		Y;
+};
+
+typedef struct _PTPNTCCoordinatePair PTPNTCCoordinatePair;
+
+struct _PTPNIKONCurveData {
+	char 			static_preamble[6];
+	uint8_t			XAxisStartPoint;
+	uint8_t			XAxisEndPoint;
+	uint8_t			YAxisStartPoint;
+	uint8_t			YAxisEndPoint;
+	uint8_t			MidPointIntegerPart;
+	uint8_t			MidPointDecimalPart;
+	uint8_t			NCoordinates;
+	PTPNIKONCoordinatePair	CurveCoordinates[PTP_NIKON_MaxCurvePoints];
+};
+
+typedef struct _PTPNIKONCurveData PTPNIKONCurveData;
+
+struct _PTPEKTextParams {
+	char	*title;
+	char	*line[5];
+};
+typedef struct _PTPEKTextParams PTPEKTextParams;
+
+/* Nikon Wifi profiles */
+
+struct _PTPNIKONWifiProfile {
+	/* Values valid both when reading and writing profiles */
+	char      profile_name[17];
+	uint8_t   device_type;
+	uint8_t   icon_type;
+	char      essid[33];
+
+	/* Values only valid when reading. Some of these are in the write packet,
+	 * but are set automatically, like id, display_order and creation_date. */
+	uint8_t   id;
+	uint8_t   valid;
+	uint8_t   display_order;
+	char      creation_date[16];
+	char      lastusage_date[16];
+	
+	/* Values only valid when writing */
+	uint32_t  ip_address;
+	uint8_t   subnet_mask; /* first zero bit position, e.g. 24 for 255.255.255.0 */
+	uint32_t  gateway_address;
+	uint8_t   address_mode; /* 0 - Manual, 2-3 -  DHCP ad-hoc/managed*/
+	uint8_t   access_mode; /* 0 - Managed, 1 - Adhoc */
+	uint8_t   wifi_channel; /* 1-11 */
+	uint8_t   authentification; /* 0 - Open, 1 - Shared, 2 - WPA-PSK */
+	uint8_t   encryption; /* 0 - None, 1 - WEP 64bit, 2 - WEP 128bit (not supported: 3 - TKIP) */
+	uint8_t   key[64];
+	uint8_t   key_nr;
+//	char      guid[16];
+};
+
+typedef struct _PTPNIKONWifiProfile PTPNIKONWifiProfile;
+
+#define PTP_CANON_EOS_CHANGES_TYPE_UNKNOWN		0
+#define PTP_CANON_EOS_CHANGES_TYPE_OBJECTINFO		1
+#define PTP_CANON_EOS_CHANGES_TYPE_OBJECTTRANSFER	2
+
+struct _PTPCanon_New_Object {
+	uint32_t		oid;
+	PTPObjectInfo	oi;
+};
+
+struct _PTPCanon_changes_entry {
+	int	type;
+	union {
+		struct _PTPCanon_New_Object	object;	/* TYPE_OBJECTINFO */
+	} u;
+};
+typedef struct _PTPCanon_changes_entry PTPCanon_changes_entry;
+
+typedef struct _PTPCanon_Property {
+	uint32_t		size;
+	uint32_t		type;
+	uint32_t		proptype;
+	unsigned char		*data;
+
+	/* fill out for queries */
+	PTPDevicePropDesc	dpd;
+} PTPCanon_Property;
+
+typedef struct _PTPCanonEOSDeviceInfo {
+	/* length */
+	uint32_t EventsSupported_len;
+	uint32_t *EventsSupported;
+
+	uint32_t DevicePropertiesSupported_len;
+	uint32_t *DevicePropertiesSupported;
+
+	uint32_t unk_len;
+	uint32_t *unk;
+} PTPCanonEOSDeviceInfo;
+
+/* DataType Codes */
+
+#define PTP_DTC_UNDEF		0x0000
+#define PTP_DTC_INT8		0x0001
+#define PTP_DTC_UINT8		0x0002
+#define PTP_DTC_INT16		0x0003
+#define PTP_DTC_UINT16		0x0004
+#define PTP_DTC_INT32		0x0005
+#define PTP_DTC_UINT32		0x0006
+#define PTP_DTC_INT64		0x0007
+#define PTP_DTC_UINT64		0x0008
+#define PTP_DTC_INT128		0x0009
+#define PTP_DTC_UINT128		0x000A
+
+#define PTP_DTC_ARRAY_MASK	0x4000
+
+#define PTP_DTC_AINT8		(PTP_DTC_ARRAY_MASK | PTP_DTC_INT8)
+#define PTP_DTC_AUINT8		(PTP_DTC_ARRAY_MASK | PTP_DTC_UINT8)
+#define PTP_DTC_AINT16		(PTP_DTC_ARRAY_MASK | PTP_DTC_INT16)
+#define PTP_DTC_AUINT16		(PTP_DTC_ARRAY_MASK | PTP_DTC_UINT16)
+#define PTP_DTC_AINT32		(PTP_DTC_ARRAY_MASK | PTP_DTC_INT32)
+#define PTP_DTC_AUINT32		(PTP_DTC_ARRAY_MASK | PTP_DTC_UINT32)
+#define PTP_DTC_AINT64		(PTP_DTC_ARRAY_MASK | PTP_DTC_INT64)
+#define PTP_DTC_AUINT64		(PTP_DTC_ARRAY_MASK | PTP_DTC_UINT64)
+#define PTP_DTC_AINT128		(PTP_DTC_ARRAY_MASK | PTP_DTC_INT128)
+#define PTP_DTC_AUINT128	(PTP_DTC_ARRAY_MASK | PTP_DTC_UINT128)
+
+#define PTP_DTC_STR		0xFFFF
+
+/* Device Properties Codes */
+
+/* PTP v1.0 property codes */
+#define PTP_DPC_Undefined		0x5000
+#define PTP_DPC_BatteryLevel		0x5001
+#define PTP_DPC_FunctionalMode		0x5002
+#define PTP_DPC_ImageSize		0x5003
+#define PTP_DPC_CompressionSetting	0x5004
+#define PTP_DPC_WhiteBalance		0x5005
+#define PTP_DPC_RGBGain			0x5006
+#define PTP_DPC_FNumber			0x5007
+#define PTP_DPC_FocalLength		0x5008
+#define PTP_DPC_FocusDistance		0x5009
+#define PTP_DPC_FocusMode		0x500A
+#define PTP_DPC_ExposureMeteringMode	0x500B
+#define PTP_DPC_FlashMode		0x500C
+#define PTP_DPC_ExposureTime		0x500D
+#define PTP_DPC_ExposureProgramMode	0x500E
+#define PTP_DPC_ExposureIndex		0x500F
+#define PTP_DPC_ExposureBiasCompensation	0x5010
+#define PTP_DPC_DateTime		0x5011
+#define PTP_DPC_CaptureDelay		0x5012
+#define PTP_DPC_StillCaptureMode	0x5013
+#define PTP_DPC_Contrast		0x5014
+#define PTP_DPC_Sharpness		0x5015
+#define PTP_DPC_DigitalZoom		0x5016
+#define PTP_DPC_EffectMode		0x5017
+#define PTP_DPC_BurstNumber		0x5018
+#define PTP_DPC_BurstInterval		0x5019
+#define PTP_DPC_TimelapseNumber		0x501A
+#define PTP_DPC_TimelapseInterval	0x501B
+#define PTP_DPC_FocusMeteringMode	0x501C
+#define PTP_DPC_UploadURL		0x501D
+#define PTP_DPC_Artist			0x501E
+#define PTP_DPC_CopyrightInfo		0x501F
+/* PTP v1.1 property codes */
+#define PTP_DPC_SupportedStreams	0x5020
+#define PTP_DPC_EnabledStreams		0x5021
+#define PTP_DPC_VideoFormat		0x5022
+#define PTP_DPC_VideoResolution		0x5023
+#define PTP_DPC_VideoQuality		0x5024
+#define PTP_DPC_VideoFrameRate		0x5025
+#define PTP_DPC_VideoContrast		0x5026
+#define PTP_DPC_VideoBrightness		0x5027
+#define PTP_DPC_AudioFormat		0x5028
+#define PTP_DPC_AudioBitrate		0x5029
+#define PTP_DPC_AudioSamplingRate	0x502A
+#define PTP_DPC_AudioBitPerSample	0x502B
+#define PTP_DPC_AudioVolume		0x502C
+
+/* Proprietary vendor extension device property mask */
+#define PTP_DPC_EXTENSION_MASK		0xF000
+#define PTP_DPC_EXTENSION		0xD000
+
+/* Zune extension device property codes */
+#define PTP_DPC_MTP_ZUNE_UNKNOWN1	0xD181
+#define PTP_DPC_MTP_ZUNE_UNKNOWN2	0xD132
+#define PTP_DPC_MTP_ZUNE_UNKNOWN3	0xD215
+#define PTP_DPC_MTP_ZUNE_UNKNOWN4	0xD216
+
+/* Eastman Kodak extension device property codes */
+#define PTP_DPC_EK_ColorTemperature	0xD001
+#define PTP_DPC_EK_DateTimeStampFormat	0xD002
+#define PTP_DPC_EK_BeepMode		0xD003
+#define PTP_DPC_EK_VideoOut		0xD004
+#define PTP_DPC_EK_PowerSaving		0xD005
+#define PTP_DPC_EK_UI_Language		0xD006
+
+/* Canon extension device property codes */
+#define PTP_DPC_CANON_BeepMode		0xD001
+#define PTP_DPC_CANON_BatteryKind	0xD002
+#define PTP_DPC_CANON_BatteryStatus	0xD003
+#define PTP_DPC_CANON_UILockType	0xD004
+#define PTP_DPC_CANON_CameraMode	0xD005
+#define PTP_DPC_CANON_ImageQuality	0xD006
+#define PTP_DPC_CANON_FullViewFileFormat 0xD007
+#define PTP_DPC_CANON_ImageSize		0xD008
+#define PTP_DPC_CANON_SelfTime		0xD009
+#define PTP_DPC_CANON_FlashMode		0xD00A
+#define PTP_DPC_CANON_Beep		0xD00B
+#define PTP_DPC_CANON_ShootingMode	0xD00C
+#define PTP_DPC_CANON_ImageMode		0xD00D
+#define PTP_DPC_CANON_DriveMode		0xD00E
+#define PTP_DPC_CANON_EZoom		0xD00F
+#define PTP_DPC_CANON_MeteringMode	0xD010
+#define PTP_DPC_CANON_AFDistance	0xD011
+#define PTP_DPC_CANON_FocusingPoint	0xD012
+#define PTP_DPC_CANON_WhiteBalance	0xD013
+#define PTP_DPC_CANON_SlowShutterSetting	0xD014
+#define PTP_DPC_CANON_AFMode		0xD015
+#define PTP_DPC_CANON_ImageStabilization	0xD016
+#define PTP_DPC_CANON_Contrast		0xD017
+#define PTP_DPC_CANON_ColorGain		0xD018
+#define PTP_DPC_CANON_Sharpness		0xD019
+#define PTP_DPC_CANON_Sensitivity	0xD01A
+#define PTP_DPC_CANON_ParameterSet	0xD01B
+#define PTP_DPC_CANON_ISOSpeed		0xD01C
+#define PTP_DPC_CANON_Aperture		0xD01D
+#define PTP_DPC_CANON_ShutterSpeed	0xD01E
+#define PTP_DPC_CANON_ExpCompensation	0xD01F
+#define PTP_DPC_CANON_FlashCompensation	0xD020
+#define PTP_DPC_CANON_AEBExposureCompensation	0xD021
+#define PTP_DPC_CANON_AvOpen		0xD023
+#define PTP_DPC_CANON_AvMax		0xD024
+#define PTP_DPC_CANON_FocalLength	0xD025
+#define PTP_DPC_CANON_FocalLengthTele	0xD026
+#define PTP_DPC_CANON_FocalLengthWide	0xD027
+#define PTP_DPC_CANON_FocalLengthDenominator	0xD028
+#define PTP_DPC_CANON_CaptureTransferMode	0xD029
+#define CANON_TRANSFER_ENTIRE_IMAGE_TO_PC	0x0002
+#define CANON_TRANSFER_SAVE_THUMBNAIL_TO_DEVICE	0x0004
+#define CANON_TRANSFER_SAVE_IMAGE_TO_DEVICE	0x0008
+/* we use those values: */
+#define CANON_TRANSFER_MEMORY		(2|1)
+#define CANON_TRANSFER_CARD		(8|4|1)
+
+#define PTP_DPC_CANON_Zoom		0xD02A
+#define PTP_DPC_CANON_NamePrefix	0xD02B
+#define PTP_DPC_CANON_SizeQualityMode	0xD02C
+#define PTP_DPC_CANON_SupportedThumbSize	0xD02D
+#define PTP_DPC_CANON_SizeOfOutputDataFromCamera	0xD02E
+#define PTP_DPC_CANON_SizeOfInputDataToCamera		0xD02F
+#define PTP_DPC_CANON_RemoteAPIVersion	0xD030
+#define PTP_DPC_CANON_FirmwareVersion	0xD031
+#define PTP_DPC_CANON_CameraModel	0xD032
+#define PTP_DPC_CANON_CameraOwner	0xD033
+#define PTP_DPC_CANON_UnixTime		0xD034
+#define PTP_DPC_CANON_CameraBodyID	0xD035
+#define PTP_DPC_CANON_CameraOutput	0xD036
+#define PTP_DPC_CANON_DispAv		0xD037
+#define PTP_DPC_CANON_AvOpenApex	0xD038
+#define PTP_DPC_CANON_DZoomMagnification	0xD039
+#define PTP_DPC_CANON_MlSpotPos		0xD03A
+#define PTP_DPC_CANON_DispAvMax		0xD03B
+#define PTP_DPC_CANON_AvMaxApex		0xD03C
+#define PTP_DPC_CANON_EZoomStartPosition		0xD03D
+#define PTP_DPC_CANON_FocalLengthOfTele	0xD03E
+#define PTP_DPC_CANON_EZoomSizeOfTele	0xD03F
+#define PTP_DPC_CANON_PhotoEffect	0xD040
+#define PTP_DPC_CANON_AssistLight	0xD041
+#define PTP_DPC_CANON_FlashQuantityCount	0xD042
+#define PTP_DPC_CANON_RotationAngle	0xD043
+#define PTP_DPC_CANON_RotationScene	0xD044
+#define PTP_DPC_CANON_EventEmulateMode	0xD045
+#define PTP_DPC_CANON_DPOFVersion	0xD046
+#define PTP_DPC_CANON_TypeOfSupportedSlideShow	0xD047
+#define PTP_DPC_CANON_AverageFilesizes	0xD048
+#define PTP_DPC_CANON_ModelID		0xD049
+
+/* From EOS 400D trace. */
+#define PTP_DPC_CANON_EOS_Aperture		0xD101
+#define PTP_DPC_CANON_EOS_ShutterSpeed		0xD102
+#define PTP_DPC_CANON_EOS_ISOSpeed		0xD103
+#define PTP_DPC_CANON_EOS_ExpCompensation	0xD104
+#define PTP_DPC_CANON_EOS_AutoExposureMode	0xD105
+#define PTP_DPC_CANON_EOS_DriveMode		0xD106
+#define PTP_DPC_CANON_EOS_MeteringMode		0xD107 
+#define PTP_DPC_CANON_EOS_FocusMode		0xD108
+#define PTP_DPC_CANON_EOS_WhiteBalance		0xD109
+#define PTP_DPC_CANON_EOS_ColorTemperature	0xD10A
+#define PTP_DPC_CANON_EOS_WhiteBalanceAdjustA	0xD10B
+#define PTP_DPC_CANON_EOS_WhiteBalanceAdjustB	0xD10C
+#define PTP_DPC_CANON_EOS_WhiteBalanceXA	0xD10D
+#define PTP_DPC_CANON_EOS_WhiteBalanceXB	0xD10E
+#define PTP_DPC_CANON_EOS_ColorSpace		0xD10F
+#define PTP_DPC_CANON_EOS_PictureStyle		0xD110
+#define PTP_DPC_CANON_EOS_BatteryPower		0xD111
+#define PTP_DPC_CANON_EOS_BatterySelect		0xD112
+#define PTP_DPC_CANON_EOS_CameraTime		0xD113
+#define PTP_DPC_CANON_EOS_Owner			0xD115
+#define PTP_DPC_CANON_EOS_ModelID		0xD116
+#define PTP_DPC_CANON_EOS_PTPExtensionVersion	0xD119
+#define PTP_DPC_CANON_EOS_DPOFVersion		0xD11A
+#define PTP_DPC_CANON_EOS_AvailableShots	0xD11B
+#define PTP_DPC_CANON_EOS_CaptureDestination	0xD11C
+#define PTP_DPC_CANON_EOS_BracketMode		0xD11D
+#define PTP_DPC_CANON_EOS_CurrentStorage	0xD11E
+#define PTP_DPC_CANON_EOS_CurrentFolder		0xD11F
+#define PTP_DPC_CANON_EOS_ImageFormat		0xD120	/* file setting */
+#define PTP_DPC_CANON_EOS_ImageFormatCF		0xD121	/* file setting CF */
+#define PTP_DPC_CANON_EOS_ImageFormatSD		0xD122	/* file setting SD */
+#define PTP_DPC_CANON_EOS_ImageFormatExtHD	0xD123	/* file setting exthd */
+#define PTP_DPC_CANON_EOS_CompressionS		0xD130
+#define PTP_DPC_CANON_EOS_CompressionM1		0xD131
+#define PTP_DPC_CANON_EOS_CompressionM2		0xD132
+#define PTP_DPC_CANON_EOS_CompressionL		0xD133
+#define PTP_DPC_CANON_EOS_PCWhiteBalance1	0xD140
+#define PTP_DPC_CANON_EOS_PCWhiteBalance2	0xD141
+#define PTP_DPC_CANON_EOS_PCWhiteBalance3	0xD142
+#define PTP_DPC_CANON_EOS_PCWhiteBalance4	0xD143
+#define PTP_DPC_CANON_EOS_PCWhiteBalance5	0xD144
+#define PTP_DPC_CANON_EOS_MWhiteBalance		0xD145
+#define PTP_DPC_CANON_EOS_PictureStyleStandard	0xD150
+#define PTP_DPC_CANON_EOS_PictureStylePortrait	0xD151
+#define PTP_DPC_CANON_EOS_PictureStyleLandscape	0xD152
+#define PTP_DPC_CANON_EOS_PictureStyleNeutral	0xD153
+#define PTP_DPC_CANON_EOS_PictureStyleFaithful	0xD154
+#define PTP_DPC_CANON_EOS_PictureStyleBlackWhite	0xD155
+#define PTP_DPC_CANON_EOS_PictureStyleUserSet1	0xD160
+#define PTP_DPC_CANON_EOS_PictureStyleUserSet2	0xD161
+#define PTP_DPC_CANON_EOS_PictureStyleUserSet3	0xD162
+#define PTP_DPC_CANON_EOS_PictureStyleParam1	0xD170
+#define PTP_DPC_CANON_EOS_PictureStyleParam2	0xD171
+#define PTP_DPC_CANON_EOS_PictureStyleParam3	0xD172
+#define PTP_DPC_CANON_EOS_FlavorLUTParams	0xD17f
+#define PTP_DPC_CANON_EOS_CustomFunc1		0xD180
+#define PTP_DPC_CANON_EOS_CustomFunc2		0xD181
+#define PTP_DPC_CANON_EOS_CustomFunc3		0xD182
+#define PTP_DPC_CANON_EOS_CustomFunc4		0xD183
+#define PTP_DPC_CANON_EOS_CustomFunc5		0xD184
+#define PTP_DPC_CANON_EOS_CustomFunc6		0xD185
+#define PTP_DPC_CANON_EOS_CustomFunc7		0xD186
+#define PTP_DPC_CANON_EOS_CustomFunc8		0xD187
+#define PTP_DPC_CANON_EOS_CustomFunc9		0xD188
+#define PTP_DPC_CANON_EOS_CustomFunc10		0xD189
+#define PTP_DPC_CANON_EOS_CustomFunc11		0xD18a
+#define PTP_DPC_CANON_EOS_CustomFunc12		0xD18b
+#define PTP_DPC_CANON_EOS_CustomFunc13		0xD18c
+#define PTP_DPC_CANON_EOS_CustomFunc14		0xD18d
+#define PTP_DPC_CANON_EOS_CustomFunc15		0xD18e
+#define PTP_DPC_CANON_EOS_CustomFunc16		0xD18f
+#define PTP_DPC_CANON_EOS_CustomFunc17		0xD190
+#define PTP_DPC_CANON_EOS_CustomFunc18		0xD191
+#define PTP_DPC_CANON_EOS_CustomFunc19		0xD192
+#define PTP_DPC_CANON_EOS_CustomFunc19		0xD192
+#define PTP_DPC_CANON_EOS_CustomFuncEx		0xD1a0
+#define PTP_DPC_CANON_EOS_MyMenu		0xD1a1
+#define PTP_DPC_CANON_EOS_MyMenuList		0xD1a2
+#define PTP_DPC_CANON_EOS_WftStatus		0xD1a3
+#define PTP_DPC_CANON_EOS_WftInputTransmission	0xD1a4
+#define PTP_DPC_CANON_EOS_HDDirectoryStructure	0xD1a5
+#define PTP_DPC_CANON_EOS_BatteryInfo		0xD1a6
+#define PTP_DPC_CANON_EOS_AdapterInfo		0xD1a7
+#define PTP_DPC_CANON_EOS_LensStatus		0xD1a8
+#define PTP_DPC_CANON_EOS_QuickReviewTime	0xD1a9
+#define PTP_DPC_CANON_EOS_CardExtension		0xD1aa
+#define PTP_DPC_CANON_EOS_TempStatus		0xD1ab
+#define PTP_DPC_CANON_EOS_ShutterCounter	0xD1ac
+#define PTP_DPC_CANON_EOS_SpecialOption		0xD1ad
+#define PTP_DPC_CANON_EOS_PhotoStudioMode	0xD1ae
+#define PTP_DPC_CANON_EOS_SerialNumber		0xD1af
+#define PTP_DPC_CANON_EOS_EVFOutputDevice	0xD1b0
+#define PTP_DPC_CANON_EOS_EVFMode		0xD1b1
+#define PTP_DPC_CANON_EOS_DepthOfFieldPreview	0xD1b2
+#define PTP_DPC_CANON_EOS_EVFSharpness		0xD1b3
+#define PTP_DPC_CANON_EOS_EVFWBMode		0xD1b4
+#define PTP_DPC_CANON_EOS_EVFClickWBCoeffs	0xD1b5
+#define PTP_DPC_CANON_EOS_EVFColorTemp		0xD1b6
+#define PTP_DPC_CANON_EOS_ExposureSimMode	0xD1b7
+#define PTP_DPC_CANON_EOS_EVFRecordStatus	0xD1b8
+#define PTP_DPC_CANON_EOS_LvAfSystem		0xD1ba
+#define PTP_DPC_CANON_EOS_MovSize		0xD1bb
+#define PTP_DPC_CANON_EOS_LvViewTypeSelect	0xD1bc
+#define PTP_DPC_CANON_EOS_Artist		0xD1d0
+#define PTP_DPC_CANON_EOS_Copyright		0xD1d1
+#define PTP_DPC_CANON_EOS_BracketValue		0xD1d2
+#define PTP_DPC_CANON_EOS_FocusInfoEx		0xD1d3
+#define PTP_DPC_CANON_EOS_DepthOfField		0xD1d4
+#define PTP_DPC_CANON_EOS_Brightness		0xD1d5
+#define PTP_DPC_CANON_EOS_LensAdjustParams	0xD1d6
+#define PTP_DPC_CANON_EOS_EFComp		0xD1d7
+#define PTP_DPC_CANON_EOS_LensName		0xD1d8
+#define PTP_DPC_CANON_EOS_AEB			0xD1d9
+#define PTP_DPC_CANON_EOS_StroboSetting		0xD1da
+#define PTP_DPC_CANON_EOS_StroboWirelessSetting	0xD1db
+#define PTP_DPC_CANON_EOS_StroboFiring		0xD1dc
+#define PTP_DPC_CANON_EOS_LensID		0xD1dd
+
+/* Nikon extension device property codes */
+#define PTP_DPC_NIKON_ShootingBank			0xD010
+#define PTP_DPC_NIKON_ShootingBankNameA 		0xD011
+#define PTP_DPC_NIKON_ShootingBankNameB			0xD012
+#define PTP_DPC_NIKON_ShootingBankNameC			0xD013
+#define PTP_DPC_NIKON_ShootingBankNameD			0xD014
+#define PTP_DPC_NIKON_ResetBank0			0xD015
+#define PTP_DPC_NIKON_RawCompression			0xD016
+#define PTP_DPC_NIKON_WhiteBalanceAutoBias		0xD017
+#define PTP_DPC_NIKON_WhiteBalanceTungstenBias		0xD018
+#define PTP_DPC_NIKON_WhiteBalanceFluorescentBias	0xD019
+#define PTP_DPC_NIKON_WhiteBalanceDaylightBias		0xD01A
+#define PTP_DPC_NIKON_WhiteBalanceFlashBias		0xD01B
+#define PTP_DPC_NIKON_WhiteBalanceCloudyBias		0xD01C
+#define PTP_DPC_NIKON_WhiteBalanceShadeBias		0xD01D
+#define PTP_DPC_NIKON_WhiteBalanceColorTemperature	0xD01E
+#define PTP_DPC_NIKON_WhiteBalancePresetNo		0xD01F
+#define PTP_DPC_NIKON_WhiteBalancePresetName0		0xD020
+#define PTP_DPC_NIKON_WhiteBalancePresetName1		0xD021
+#define PTP_DPC_NIKON_WhiteBalancePresetName2		0xD022
+#define PTP_DPC_NIKON_WhiteBalancePresetName3		0xD023
+#define PTP_DPC_NIKON_WhiteBalancePresetName4		0xD024
+#define PTP_DPC_NIKON_WhiteBalancePresetVal0		0xD025
+#define PTP_DPC_NIKON_WhiteBalancePresetVal1		0xD026
+#define PTP_DPC_NIKON_WhiteBalancePresetVal2		0xD027
+#define PTP_DPC_NIKON_WhiteBalancePresetVal3		0xD028
+#define PTP_DPC_NIKON_WhiteBalancePresetVal4		0xD029
+#define PTP_DPC_NIKON_ImageSharpening			0xD02A
+#define PTP_DPC_NIKON_ToneCompensation			0xD02B
+#define PTP_DPC_NIKON_ColorModel			0xD02C
+#define PTP_DPC_NIKON_HueAdjustment			0xD02D
+#define PTP_DPC_NIKON_NonCPULensDataFocalLength		0xD02E	/* Set FMM Manual */
+#define PTP_DPC_NIKON_NonCPULensDataMaximumAperture	0xD02F	/* Set F0 Manual */
+#define PTP_DPC_NIKON_ShootingMode			0xD030
+#define PTP_DPC_NIKON_JPEG_Compression_Policy		0xD031
+#define PTP_DPC_NIKON_ColorSpace			0xD032
+#define PTP_DPC_NIKON_AutoDXCrop			0xD033
+#define PTP_DPC_NIKON_CSMMenuBankSelect			0xD040
+#define PTP_DPC_NIKON_MenuBankNameA			0xD041
+#define PTP_DPC_NIKON_MenuBankNameB			0xD042
+#define PTP_DPC_NIKON_MenuBankNameC			0xD043
+#define PTP_DPC_NIKON_MenuBankNameD			0xD044
+#define PTP_DPC_NIKON_ResetBank				0xD045
+#define PTP_DPC_NIKON_A1AFCModePriority			0xD048
+#define PTP_DPC_NIKON_A2AFSModePriority			0xD049
+#define PTP_DPC_NIKON_A3GroupDynamicAF			0xD04A
+#define PTP_DPC_NIKON_A4AFActivation			0xD04B
+#define PTP_DPC_NIKON_FocusAreaIllumManualFocus		0xD04C
+#define PTP_DPC_NIKON_FocusAreaIllumContinuous		0xD04D
+#define PTP_DPC_NIKON_FocusAreaIllumWhenSelected 	0xD04E
+#define PTP_DPC_NIKON_FocusAreaWrap			0xD04F /* area sel */
+#define PTP_DPC_NIKON_VerticalAFON			0xD050
+#define PTP_DPC_NIKON_AFLockOn				0xD051
+#define PTP_DPC_NIKON_FocusAreaZone			0xD052
+#define PTP_DPC_NIKON_EnableCopyright			0xD053
+#define PTP_DPC_NIKON_ISOAuto				0xD054
+#define PTP_DPC_NIKON_EVISOStep				0xD055
+#define PTP_DPC_NIKON_EVStep				0xD056 /* EV Step SS FN */
+#define PTP_DPC_NIKON_EVStepExposureComp		0xD057
+#define PTP_DPC_NIKON_ExposureCompensation		0xD058
+#define PTP_DPC_NIKON_CenterWeightArea			0xD059
+#define PTP_DPC_NIKON_ExposureBaseMatrix		0xD05A
+#define PTP_DPC_NIKON_ExposureBaseCenter		0xD05B
+#define PTP_DPC_NIKON_ExposureBaseSpot			0xD05C
+#define PTP_DPC_NIKON_LiveViewAF			0xD05D
+#define PTP_DPC_NIKON_AELockMode			0xD05E
+#define PTP_DPC_NIKON_AELAFLMode			0xD05F
+#define PTP_DPC_NIKON_MeterOff				0xD062
+#define PTP_DPC_NIKON_SelfTimer				0xD063
+#define PTP_DPC_NIKON_MonitorOff			0xD064
+#define PTP_DPC_NIKON_ImgConfTime			0xD065
+#define PTP_DPC_NIKON_AngleLevel			0xD067
+#define PTP_DPC_NIKON_D1ShootingSpeed			0xD068 /* continous speed low */
+#define PTP_DPC_NIKON_D2MaximumShots			0xD069
+#define PTP_DPC_NIKON_D3ExpDelayMode			0xD06A
+#define PTP_DPC_NIKON_LongExposureNoiseReduction	0xD06B
+#define PTP_DPC_NIKON_FileNumberSequence		0xD06C
+#define PTP_DPC_NIKON_ControlPanelFinderRearControl	0xD06D
+#define PTP_DPC_NIKON_ControlPanelFinderViewfinder	0xD06E
+#define PTP_DPC_NIKON_D7Illumination			0xD06F
+#define PTP_DPC_NIKON_NrHighISO				0xD070
+#define PTP_DPC_NIKON_SHSET_CH_GUID_DISP		0xD071
+#define PTP_DPC_NIKON_ArtistName			0xD072
+#define PTP_DPC_NIKON_CopyrightInfo			0xD073
+#define PTP_DPC_NIKON_FlashSyncSpeed			0xD074
+#define PTP_DPC_NIKON_FlashShutterSpeed			0xD075	/* SB Low Limit */
+#define PTP_DPC_NIKON_E3AAFlashMode			0xD076
+#define PTP_DPC_NIKON_E4ModelingFlash			0xD077
+#define PTP_DPC_NIKON_BracketSet			0xD078	/* Bracket Type? */
+#define PTP_DPC_NIKON_E6ManualModeBracketing		0xD079	/* Bracket Factor? */
+#define PTP_DPC_NIKON_BracketOrder			0xD07A
+#define PTP_DPC_NIKON_E8AutoBracketSelection		0xD07B	/* Bracket Method? */
+#define PTP_DPC_NIKON_BracketingSet			0xD07C
+#define PTP_DPC_NIKON_F1CenterButtonShootingMode	0xD080
+#define PTP_DPC_NIKON_CenterButtonPlaybackMode		0xD081
+#define PTP_DPC_NIKON_F2Multiselector			0xD082
+#define PTP_DPC_NIKON_F3PhotoInfoPlayback		0xD083	/* MultiSelector Dir */
+#define PTP_DPC_NIKON_F4AssignFuncButton		0xD084  /* CMD Dial Rotate */
+#define PTP_DPC_NIKON_F5CustomizeCommDials		0xD085  /* CMD Dial Change */
+#define PTP_DPC_NIKON_ReverseCommandDial		0xD086  /* CMD Dial FN Set */
+#define PTP_DPC_NIKON_ApertureSetting			0xD087  /* CMD Dial Active */
+#define PTP_DPC_NIKON_MenusAndPlayback			0xD088  /* CMD Dial Active */
+#define PTP_DPC_NIKON_F6ButtonsAndDials			0xD089  /* Universal Mode? */
+#define PTP_DPC_NIKON_NoCFCard				0xD08A	/* Enable Shutter? */
+#define PTP_DPC_NIKON_CenterButtonZoomRatio		0xD08B
+#define PTP_DPC_NIKON_FunctionButton2			0xD08C
+#define PTP_DPC_NIKON_AFAreaPoint			0xD08D
+#define PTP_DPC_NIKON_NormalAFOn			0xD08E
+#define PTP_DPC_NIKON_ImageCommentString		0xD090
+#define PTP_DPC_NIKON_ImageCommentEnable		0xD091
+#define PTP_DPC_NIKON_ImageRotation			0xD092
+#define PTP_DPC_NIKON_ManualSetLensNo			0xD093
+#define PTP_DPC_NIKON_MovScreenSize			0xD0A0
+#define PTP_DPC_NIKON_MovVoice				0xD0A1
+#define PTP_DPC_NIKON_Bracketing			0xD0C0
+#define PTP_DPC_NIKON_AutoExposureBracketStep		0xD0C1
+#define PTP_DPC_NIKON_AutoExposureBracketProgram	0xD0C2
+#define PTP_DPC_NIKON_AutoExposureBracketCount		0xD0C3
+#define PTP_DPC_NIKON_WhiteBalanceBracketStep		0xD0C4
+#define PTP_DPC_NIKON_WhiteBalanceBracketProgram	0xD0C5
+#define PTP_DPC_NIKON_LensID				0xD0E0
+#define PTP_DPC_NIKON_LensSort				0xD0E1
+#define PTP_DPC_NIKON_LensType				0xD0E2
+#define PTP_DPC_NIKON_FocalLengthMin			0xD0E3
+#define PTP_DPC_NIKON_FocalLengthMax			0xD0E4
+#define PTP_DPC_NIKON_MaxApAtMinFocalLength		0xD0E5
+#define PTP_DPC_NIKON_MaxApAtMaxFocalLength		0xD0E6
+#define PTP_DPC_NIKON_FinderISODisp			0xD0F0
+#define PTP_DPC_NIKON_AutoOffPhoto			0xD0F2
+#define PTP_DPC_NIKON_AutoOffMenu			0xD0F3
+#define PTP_DPC_NIKON_AutoOffInfo			0xD0F4
+#define PTP_DPC_NIKON_SelfTimerShootNum			0xD0F5
+#define PTP_DPC_NIKON_VignetteCtrl			0xD0F7
+#define PTP_DPC_NIKON_ExposureTime			0xD100	/* Shutter Speed */
+#define PTP_DPC_NIKON_ACPower				0xD101
+#define PTP_DPC_NIKON_WarningStatus			0xD102
+#define PTP_DPC_NIKON_MaximumShots			0xD103 /* remain shots (in RAM buffer?) */
+#define PTP_DPC_NIKON_AFLockStatus			0xD104
+#define PTP_DPC_NIKON_AELockStatus			0xD105
+#define PTP_DPC_NIKON_FVLockStatus			0xD106
+#define PTP_DPC_NIKON_AutofocusLCDTopMode2		0xD107
+#define PTP_DPC_NIKON_AutofocusArea			0xD108
+#define PTP_DPC_NIKON_FlexibleProgram			0xD109
+#define PTP_DPC_NIKON_LightMeter			0xD10A	/* Exposure Status */
+#define PTP_DPC_NIKON_RecordingMedia			0xD10B	/* Card or SDRAM */
+#define PTP_DPC_NIKON_USBSpeed				0xD10C
+#define PTP_DPC_NIKON_CCDNumber				0xD10D
+#define PTP_DPC_NIKON_CameraOrientation			0xD10E
+#define PTP_DPC_NIKON_GroupPtnType			0xD10F
+#define PTP_DPC_NIKON_FNumberLock			0xD110
+#define PTP_DPC_NIKON_ExposureApertureLock		0xD111	/* shutterspeed lock*/
+#define PTP_DPC_NIKON_TVLockSetting			0xD112
+#define PTP_DPC_NIKON_AVLockSetting			0xD113
+#define PTP_DPC_NIKON_IllumSetting			0xD114
+#define PTP_DPC_NIKON_FocusPointBright			0xD115
+#define PTP_DPC_NIKON_ExternalFlashAttached		0xD120
+#define PTP_DPC_NIKON_ExternalFlashStatus		0xD121
+#define PTP_DPC_NIKON_ExternalFlashSort			0xD122
+#define PTP_DPC_NIKON_ExternalFlashMode			0xD123
+#define PTP_DPC_NIKON_ExternalFlashCompensation		0xD124
+#define PTP_DPC_NIKON_NewExternalFlashMode		0xD125
+#define PTP_DPC_NIKON_FlashExposureCompensation		0xD126
+#define PTP_DPC_NIKON_OptimizeImage			0xD140
+#define PTP_DPC_NIKON_Saturation			0xD142
+#define PTP_DPC_NIKON_BW_FillerEffect			0xD143
+#define PTP_DPC_NIKON_BW_Sharpness			0xD144
+#define PTP_DPC_NIKON_BW_Contrast			0xD145
+#define PTP_DPC_NIKON_BW_Setting_Type			0xD146
+#define PTP_DPC_NIKON_Slot2SaveMode			0xD148
+#define PTP_DPC_NIKON_RawBitMode			0xD149
+#define PTP_DPC_NIKON_ISOAutoTime			0xD14E
+#define PTP_DPC_NIKON_FlourescentType			0xD14F
+#define PTP_DPC_NIKON_TuneColourTemperature		0xD150
+#define PTP_DPC_NIKON_TunePreset0			0xD151
+#define PTP_DPC_NIKON_TunePreset1			0xD152
+#define PTP_DPC_NIKON_TunePreset2			0xD153
+#define PTP_DPC_NIKON_TunePreset3			0xD154
+#define PTP_DPC_NIKON_TunePreset4			0xD155
+#define PTP_DPC_NIKON_BeepOff				0xD160
+#define PTP_DPC_NIKON_AutofocusMode			0xD161
+#define PTP_DPC_NIKON_AFAssist				0xD163
+#define PTP_DPC_NIKON_PADVPMode				0xD164	/* iso auto time */
+#define PTP_DPC_NIKON_ImageReview			0xD165
+#define PTP_DPC_NIKON_AFAreaIllumination		0xD166
+#define PTP_DPC_NIKON_FlashMode				0xD167
+#define PTP_DPC_NIKON_FlashCommanderMode		0xD168
+#define PTP_DPC_NIKON_FlashSign				0xD169
+#define PTP_DPC_NIKON_ISO_Auto				0xD16A
+#define PTP_DPC_NIKON_RemoteTimeout			0xD16B
+#define PTP_DPC_NIKON_GridDisplay			0xD16C
+#define PTP_DPC_NIKON_FlashModeManualPower		0xD16D
+#define PTP_DPC_NIKON_FlashModeCommanderPower		0xD16E
+#define PTP_DPC_NIKON_AutoFP				0xD16F
+#define PTP_DPC_NIKON_CSMMenu				0xD180
+#define PTP_DPC_NIKON_WarningDisplay			0xD181
+#define PTP_DPC_NIKON_BatteryCellKind			0xD182
+#define PTP_DPC_NIKON_ISOAutoHiLimit			0xD183
+#define PTP_DPC_NIKON_DynamicAFArea			0xD184
+#define PTP_DPC_NIKON_ContinuousSpeedHigh		0xD186
+#define PTP_DPC_NIKON_InfoDispSetting			0xD187
+#define PTP_DPC_NIKON_PreviewButton			0xD189
+#define PTP_DPC_NIKON_PreviewButton2			0xD18A
+#define PTP_DPC_NIKON_AEAFLockButton2			0xD18B
+#define PTP_DPC_NIKON_IndicatorDisp			0xD18D
+#define PTP_DPC_NIKON_CellKindPriority			0xD18E
+#define PTP_DPC_NIKON_BracketingFramesAndSteps		0xD190
+#define PTP_DPC_NIKON_LiveViewMode			0xD1A0
+#define PTP_DPC_NIKON_LiveViewDriveMode			0xD1A1
+#define PTP_DPC_NIKON_LiveViewStatus			0xD1A2
+#define PTP_DPC_NIKON_LiveViewImageZoomRatio		0xD1A3
+#define PTP_DPC_NIKON_LiveViewProhibitCondition		0xD1A4
+#define PTP_DPC_NIKON_ExposureDisplayStatus		0xD1B0
+#define PTP_DPC_NIKON_ExposureIndicateStatus		0xD1B1
+#define PTP_DPC_NIKON_InfoDispErrStatus			0xD1B2
+#define PTP_DPC_NIKON_ExposureIndicateLightup		0xD1B3
+#define PTP_DPC_NIKON_FlashOpen				0xD1C0
+#define PTP_DPC_NIKON_FlashCharged			0xD1C1
+#define PTP_DPC_NIKON_FlashMRepeatValue			0xD1D0
+#define PTP_DPC_NIKON_FlashMRepeatCount			0xD1D1
+#define PTP_DPC_NIKON_FlashMRepeatInterval		0xD1D2
+#define PTP_DPC_NIKON_FlashCommandChannel		0xD1D3
+#define PTP_DPC_NIKON_FlashCommandSelfMode		0xD1D4
+#define PTP_DPC_NIKON_FlashCommandSelfCompensation	0xD1D5
+#define PTP_DPC_NIKON_FlashCommandSelfValue		0xD1D6
+#define PTP_DPC_NIKON_FlashCommandAMode			0xD1D7
+#define PTP_DPC_NIKON_FlashCommandACompensation		0xD1D8
+#define PTP_DPC_NIKON_FlashCommandAValue		0xD1D9
+#define PTP_DPC_NIKON_FlashCommandBMode			0xD1DA
+#define PTP_DPC_NIKON_FlashCommandBCompensation		0xD1DB
+#define PTP_DPC_NIKON_FlashCommandBValue		0xD1DC
+#define PTP_DPC_NIKON_ActivePicCtrlItem			0xD200
+#define PTP_DPC_NIKON_ChangePicCtrlItem			0xD201
+
+/* Microsoft/MTP specific */
+#define PTP_DPC_MTP_SecureTime				0xD101
+#define PTP_DPC_MTP_DeviceCertificate			0xD102
+#define PTP_DPC_MTP_RevocationInfo			0xD103
+#define PTP_DPC_MTP_SynchronizationPartner		0xD401
+#define PTP_DPC_MTP_DeviceFriendlyName			0xD402
+#define PTP_DPC_MTP_VolumeLevel				0xD403
+#define PTP_DPC_MTP_DeviceIcon				0xD405
+#define PTP_DPC_MTP_SessionInitiatorInfo		0xD406
+#define PTP_DPC_MTP_PerceivedDeviceType			0xD407
+#define PTP_DPC_MTP_PlaybackRate                        0xD410
+#define PTP_DPC_MTP_PlaybackObject                      0xD411
+#define PTP_DPC_MTP_PlaybackContainerIndex              0xD412
+#define PTP_DPC_MTP_PlaybackPosition                    0xD413
+#define PTP_DPC_MTP_PlaysForSureID                      0xD131
+
+/* Zune specific property codes */
+#define PTP_DPC_MTP_Zune_UnknownVersion			0xD181
+
+/* MTP specific Object Properties */
+#define PTP_OPC_StorageID				0xDC01
+#define PTP_OPC_ObjectFormat				0xDC02
+#define PTP_OPC_ProtectionStatus			0xDC03
+#define PTP_OPC_ObjectSize				0xDC04
+#define PTP_OPC_AssociationType				0xDC05
+#define PTP_OPC_AssociationDesc				0xDC06
+#define PTP_OPC_ObjectFileName				0xDC07
+#define PTP_OPC_DateCreated				0xDC08
+#define PTP_OPC_DateModified				0xDC09
+#define PTP_OPC_Keywords				0xDC0A
+#define PTP_OPC_ParentObject				0xDC0B
+#define PTP_OPC_AllowedFolderContents			0xDC0C
+#define PTP_OPC_Hidden					0xDC0D
+#define PTP_OPC_SystemObject				0xDC0E
+#define PTP_OPC_PersistantUniqueObjectIdentifier	0xDC41
+#define PTP_OPC_SyncID					0xDC42
+#define PTP_OPC_PropertyBag				0xDC43
+#define PTP_OPC_Name					0xDC44
+#define PTP_OPC_CreatedBy				0xDC45
+#define PTP_OPC_Artist					0xDC46
+#define PTP_OPC_DateAuthored				0xDC47
+#define PTP_OPC_Description				0xDC48
+#define PTP_OPC_URLReference				0xDC49
+#define PTP_OPC_LanguageLocale				0xDC4A
+#define PTP_OPC_CopyrightInformation			0xDC4B
+#define PTP_OPC_Source					0xDC4C
+#define PTP_OPC_OriginLocation				0xDC4D
+#define PTP_OPC_DateAdded				0xDC4E
+#define PTP_OPC_NonConsumable				0xDC4F
+#define PTP_OPC_CorruptOrUnplayable			0xDC50
+#define PTP_OPC_ProducerSerialNumber			0xDC51
+#define PTP_OPC_RepresentativeSampleFormat		0xDC81
+#define PTP_OPC_RepresentativeSampleSize		0xDC82
+#define PTP_OPC_RepresentativeSampleHeight		0xDC83
+#define PTP_OPC_RepresentativeSampleWidth		0xDC84
+#define PTP_OPC_RepresentativeSampleDuration		0xDC85
+#define PTP_OPC_RepresentativeSampleData		0xDC86
+#define PTP_OPC_Width					0xDC87
+#define PTP_OPC_Height					0xDC88
+#define PTP_OPC_Duration				0xDC89
+#define PTP_OPC_Rating					0xDC8A
+#define PTP_OPC_Track					0xDC8B
+#define PTP_OPC_Genre					0xDC8C
+#define PTP_OPC_Credits					0xDC8D
+#define PTP_OPC_Lyrics					0xDC8E
+#define PTP_OPC_SubscriptionContentID			0xDC8F
+#define PTP_OPC_ProducedBy				0xDC90
+#define PTP_OPC_UseCount				0xDC91
+#define PTP_OPC_SkipCount				0xDC92
+#define PTP_OPC_LastAccessed				0xDC93
+#define PTP_OPC_ParentalRating				0xDC94
+#define PTP_OPC_MetaGenre				0xDC95
+#define PTP_OPC_Composer				0xDC96
+#define PTP_OPC_EffectiveRating				0xDC97
+#define PTP_OPC_Subtitle				0xDC98
+#define PTP_OPC_OriginalReleaseDate			0xDC99
+#define PTP_OPC_AlbumName				0xDC9A
+#define PTP_OPC_AlbumArtist				0xDC9B
+#define PTP_OPC_Mood					0xDC9C
+#define PTP_OPC_DRMStatus				0xDC9D
+#define PTP_OPC_SubDescription				0xDC9E
+#define PTP_OPC_IsCropped				0xDCD1
+#define PTP_OPC_IsColorCorrected			0xDCD2
+#define PTP_OPC_ImageBitDepth				0xDCD3
+#define PTP_OPC_Fnumber					0xDCD4
+#define PTP_OPC_ExposureTime				0xDCD5
+#define PTP_OPC_ExposureIndex				0xDCD6
+#define PTP_OPC_DisplayName				0xDCE0
+#define PTP_OPC_BodyText				0xDCE1
+#define PTP_OPC_Subject					0xDCE2
+#define PTP_OPC_Priority				0xDCE3
+#define PTP_OPC_GivenName				0xDD00
+#define PTP_OPC_MiddleNames				0xDD01
+#define PTP_OPC_FamilyName				0xDD02
+#define PTP_OPC_Prefix					0xDD03
+#define PTP_OPC_Suffix					0xDD04
+#define PTP_OPC_PhoneticGivenName			0xDD05
+#define PTP_OPC_PhoneticFamilyName			0xDD06
+#define PTP_OPC_EmailPrimary				0xDD07
+#define PTP_OPC_EmailPersonal1				0xDD08
+#define PTP_OPC_EmailPersonal2				0xDD09
+#define PTP_OPC_EmailBusiness1				0xDD0A
+#define PTP_OPC_EmailBusiness2				0xDD0B
+#define PTP_OPC_EmailOthers				0xDD0C
+#define PTP_OPC_PhoneNumberPrimary			0xDD0D
+#define PTP_OPC_PhoneNumberPersonal			0xDD0E
+#define PTP_OPC_PhoneNumberPersonal2			0xDD0F
+#define PTP_OPC_PhoneNumberBusiness			0xDD10
+#define PTP_OPC_PhoneNumberBusiness2			0xDD11
+#define PTP_OPC_PhoneNumberMobile			0xDD12
+#define PTP_OPC_PhoneNumberMobile2			0xDD13
+#define PTP_OPC_FaxNumberPrimary			0xDD14
+#define PTP_OPC_FaxNumberPersonal			0xDD15
+#define PTP_OPC_FaxNumberBusiness			0xDD16
+#define PTP_OPC_PagerNumber				0xDD17
+#define PTP_OPC_PhoneNumberOthers			0xDD18
+#define PTP_OPC_PrimaryWebAddress			0xDD19
+#define PTP_OPC_PersonalWebAddress			0xDD1A
+#define PTP_OPC_BusinessWebAddress			0xDD1B
+#define PTP_OPC_InstantMessengerAddress			0xDD1C
+#define PTP_OPC_InstantMessengerAddress2		0xDD1D
+#define PTP_OPC_InstantMessengerAddress3		0xDD1E
+#define PTP_OPC_PostalAddressPersonalFull		0xDD1F
+#define PTP_OPC_PostalAddressPersonalFullLine1		0xDD20
+#define PTP_OPC_PostalAddressPersonalFullLine2		0xDD21
+#define PTP_OPC_PostalAddressPersonalFullCity		0xDD22
+#define PTP_OPC_PostalAddressPersonalFullRegion		0xDD23
+#define PTP_OPC_PostalAddressPersonalFullPostalCode	0xDD24
+#define PTP_OPC_PostalAddressPersonalFullCountry	0xDD25
+#define PTP_OPC_PostalAddressBusinessFull		0xDD26
+#define PTP_OPC_PostalAddressBusinessLine1		0xDD27
+#define PTP_OPC_PostalAddressBusinessLine2		0xDD28
+#define PTP_OPC_PostalAddressBusinessCity		0xDD29
+#define PTP_OPC_PostalAddressBusinessRegion		0xDD2A
+#define PTP_OPC_PostalAddressBusinessPostalCode		0xDD2B
+#define PTP_OPC_PostalAddressBusinessCountry		0xDD2C
+#define PTP_OPC_PostalAddressOtherFull			0xDD2D
+#define PTP_OPC_PostalAddressOtherLine1			0xDD2E
+#define PTP_OPC_PostalAddressOtherLine2			0xDD2F
+#define PTP_OPC_PostalAddressOtherCity			0xDD30
+#define PTP_OPC_PostalAddressOtherRegion		0xDD31
+#define PTP_OPC_PostalAddressOtherPostalCode		0xDD32
+#define PTP_OPC_PostalAddressOtherCountry		0xDD33
+#define PTP_OPC_OrganizationName			0xDD34
+#define PTP_OPC_PhoneticOrganizationName		0xDD35
+#define PTP_OPC_Role					0xDD36
+#define PTP_OPC_Birthdate				0xDD37
+#define PTP_OPC_MessageTo				0xDD40
+#define PTP_OPC_MessageCC				0xDD41
+#define PTP_OPC_MessageBCC				0xDD42
+#define PTP_OPC_MessageRead				0xDD43
+#define PTP_OPC_MessageReceivedTime			0xDD44
+#define PTP_OPC_MessageSender				0xDD45
+#define PTP_OPC_ActivityBeginTime			0xDD50
+#define PTP_OPC_ActivityEndTime				0xDD51
+#define PTP_OPC_ActivityLocation			0xDD52
+#define PTP_OPC_ActivityRequiredAttendees		0xDD54
+#define PTP_OPC_ActivityOptionalAttendees		0xDD55
+#define PTP_OPC_ActivityResources			0xDD56
+#define PTP_OPC_ActivityAccepted			0xDD57
+#define PTP_OPC_Owner					0xDD5D
+#define PTP_OPC_Editor					0xDD5E
+#define PTP_OPC_Webmaster				0xDD5F
+#define PTP_OPC_URLSource				0xDD60
+#define PTP_OPC_URLDestination				0xDD61
+#define PTP_OPC_TimeBookmark				0xDD62
+#define PTP_OPC_ObjectBookmark				0xDD63
+#define PTP_OPC_ByteBookmark				0xDD64
+#define PTP_OPC_LastBuildDate				0xDD70
+#define PTP_OPC_TimetoLive				0xDD71
+#define PTP_OPC_MediaGUID				0xDD72
+#define PTP_OPC_TotalBitRate				0xDE91
+#define PTP_OPC_BitRateType				0xDE92
+#define PTP_OPC_SampleRate				0xDE93
+#define PTP_OPC_NumberOfChannels			0xDE94
+#define PTP_OPC_AudioBitDepth				0xDE95
+#define PTP_OPC_ScanDepth				0xDE97
+#define PTP_OPC_AudioWAVECodec				0xDE99
+#define PTP_OPC_AudioBitRate				0xDE9A
+#define PTP_OPC_VideoFourCCCodec			0xDE9B
+#define PTP_OPC_VideoBitRate				0xDE9C
+#define PTP_OPC_FramesPerThousandSeconds		0xDE9D
+#define PTP_OPC_KeyFrameDistance			0xDE9E
+#define PTP_OPC_BufferSize				0xDE9F
+#define PTP_OPC_EncodingQuality				0xDEA0
+#define PTP_OPC_EncodingProfile				0xDEA1
+#define PTP_OPC_BuyFlag					0xD901
+
+/* WiFi Provisioning MTP Extension property codes */
+#define PTP_OPC_WirelessConfigurationFile		0xB104
+
+/* Device Property Form Flag */
+
+#define PTP_DPFF_None			0x00
+#define PTP_DPFF_Range			0x01
+#define PTP_DPFF_Enumeration		0x02
+
+/* Object Property Codes used by MTP (first 3 are same as DPFF codes) */
+#define PTP_OPFF_None			0x00
+#define PTP_OPFF_Range			0x01
+#define PTP_OPFF_Enumeration		0x02
+#define PTP_OPFF_DateTime		0x03
+#define PTP_OPFF_FixedLengthArray	0x04
+#define PTP_OPFF_RegularExpression	0x05
+#define PTP_OPFF_ByteArray		0x06
+#define PTP_OPFF_LongString		0xFF
+
+/* Device Property GetSet type */
+#define PTP_DPGS_Get			0x00
+#define PTP_DPGS_GetSet			0x01
+
+/* Glue stuff starts here */
+
+typedef struct _PTPParams PTPParams;
+
+
+typedef uint16_t (* PTPDataGetFunc)	(PTPParams* params, void*priv,
+					unsigned long wantlen,
+	                                unsigned char *data, unsigned long *gotlen);
+
+typedef uint16_t (* PTPDataPutFunc)	(PTPParams* params, void*priv,
+					unsigned long sendlen,
+	                                unsigned char *data, unsigned long *putlen);
+typedef struct _PTPDataHandler {
+	PTPDataGetFunc		getfunc;
+	PTPDataPutFunc		putfunc;
+	void			*priv;
+} PTPDataHandler;
+
+/*
+ * This functions take PTP oriented arguments and send them over an
+ * appropriate data layer doing byteorder conversion accordingly.
+ */
+typedef uint16_t (* PTPIOSendReq)	(PTPParams* params, PTPContainer* req);
+typedef uint16_t (* PTPIOSendData)	(PTPParams* params, PTPContainer* ptp,
+					 unsigned long size, PTPDataHandler*getter);
+
+typedef uint16_t (* PTPIOGetResp)	(PTPParams* params, PTPContainer* resp);
+typedef uint16_t (* PTPIOGetData)	(PTPParams* params, PTPContainer* ptp,
+	                                 PTPDataHandler *putter);
+typedef uint16_t (* PTPIOCancelReq)	(PTPParams* params, uint32_t transaction_id);
+
+/* debug functions */
+typedef void (* PTPErrorFunc) (void *data, const char *format, va_list args)
+#if (__GNUC__ >= 3)
+	__attribute__((__format__(printf,2,0)))
+#endif
+;
+typedef void (* PTPDebugFunc) (void *data, const char *format, va_list args)
+#if (__GNUC__ >= 3)
+	__attribute__((__format__(printf,2,0)))
+#endif
+;
+
+struct _PTPObject {
+	uint32_t	oid;
+	unsigned int	flags;
+#define PTPOBJECT_OBJECTINFO_LOADED	(1<<0)
+#define PTPOBJECT_CANONFLAGS_LOADED	(1<<1)
+#define PTPOBJECT_MTPPROPLIST_LOADED	(1<<2)
+#define PTPOBJECT_DIRECTORY_LOADED	(1<<3)
+#define PTPOBJECT_PARENTOBJECT_LOADED	(1<<4)
+#define PTPOBJECT_STORAGEID_LOADED	(1<<5)
+
+	PTPObjectInfo	oi;
+	uint32_t	canon_flags;
+	MTPProperties	*mtpprops;
+	int		nrofmtpprops;
+};
+typedef struct _PTPObject PTPObject;
+
+struct _PTPParams {
+	/* device flags */
+	uint32_t	device_flags;
+
+	/* data layer byteorder */
+	uint8_t		byteorder;
+	uint16_t	maxpacketsize;
+
+	/* PTP IO: Custom IO functions */
+	PTPIOSendReq	sendreq_func;
+	PTPIOSendData	senddata_func;
+	PTPIOGetResp	getresp_func;
+	PTPIOGetData	getdata_func;
+	PTPIOGetResp	event_check;
+	PTPIOGetResp	event_wait;
+	PTPIOCancelReq	cancelreq_func;
+
+	/* Custom error and debug function */
+	PTPErrorFunc	error_func;
+	PTPDebugFunc	debug_func;
+
+	/* Data passed to above functions */
+	void		*data;
+
+	/* ptp transaction ID */
+	uint32_t	transaction_id;
+	/* ptp session ID */
+	uint32_t	session_id;
+
+	/* PTP IO: if we have MTP style split header/data transfers */
+	int		split_header_data;
+
+	/* PTP: internal structures used by ptp driver */
+	PTPObject	*objects;
+	int		nrofobjects;
+
+	PTPDeviceInfo	deviceinfo;
+
+	/* PTP: the current event queue */
+	PTPContainer	*events;
+	int		nrofevents;
+
+	/* PTP: Canon specific flags list */
+	PTPCanon_Property	*canon_props;
+	int			nrofcanon_props;
+	int			canon_viewfinder_on;
+
+	/* PTP: Canon EOS event queue */
+	PTPCanon_changes_entry	*backlogentries;
+	int			nrofbacklogentries;
+	int			eos_captureenabled;
+
+	/* PTP: Wifi profiles */
+	uint8_t 	wifi_profiles_version;
+	uint8_t		wifi_profiles_number;
+	PTPNIKONWifiProfile *wifi_profiles;
+
+	/* IO: PTP/IP related data */
+	int		cmdfd, evtfd;
+	uint8_t		cameraguid[16];
+	uint32_t	eventpipeid;
+	char		*cameraname;
+
+#ifdef HAVE_ICONV
+	/* PTP: iconv converters */
+	iconv_t	cd_locale_to_ucs2;
+	iconv_t cd_ucs2_to_locale;
+#endif
+
+	/* IO: Sometimes the response packet get send in the dataphase
+	 * too. This only happens for a Samsung player now.
+	 */
+	uint8_t		*response_packet;
+	uint16_t	response_packet_size;
+};
+
+/* last, but not least - ptp functions */
+uint16_t ptp_usb_sendreq	(PTPParams* params, PTPContainer* req);
+uint16_t ptp_usb_senddata	(PTPParams* params, PTPContainer* ptp,
+				 unsigned long size, PTPDataHandler *handler);
+uint16_t ptp_usb_getresp	(PTPParams* params, PTPContainer* resp);
+uint16_t ptp_usb_getdata	(PTPParams* params, PTPContainer* ptp, 
+	                         PTPDataHandler *handler);
+uint16_t ptp_usb_event_check	(PTPParams* params, PTPContainer* event);
+uint16_t ptp_usb_event_wait	(PTPParams* params, PTPContainer* event);
+
+uint16_t ptp_usb_control_get_extended_event_data (PTPParams *params, char *buffer, int *size);
+uint16_t ptp_usb_control_device_reset_request (PTPParams *params);
+uint16_t ptp_usb_control_get_device_status (PTPParams *params, char *buffer, int *size);
+uint16_t ptp_usb_control_cancel_request (PTPParams *params, uint32_t transid);
+
+
+int      ptp_ptpip_connect	(PTPParams* params, const char *port);
+uint16_t ptp_ptpip_sendreq	(PTPParams* params, PTPContainer* req);
+uint16_t ptp_ptpip_senddata	(PTPParams* params, PTPContainer* ptp,
+				unsigned long size, PTPDataHandler *handler);
+uint16_t ptp_ptpip_getresp	(PTPParams* params, PTPContainer* resp);
+uint16_t ptp_ptpip_getdata	(PTPParams* params, PTPContainer* ptp, 
+	                         PTPDataHandler *handler);
+uint16_t ptp_ptpip_event_wait	(PTPParams* params, PTPContainer* event);
+uint16_t ptp_ptpip_event_check	(PTPParams* params, PTPContainer* event);
+
+uint16_t ptp_getdeviceinfo	(PTPParams* params, PTPDeviceInfo* deviceinfo);
+
+uint16_t ptp_generic_no_data	(PTPParams* params, uint16_t opcode, unsigned int cnt, ...);
+
+uint16_t ptp_opensession	(PTPParams *params, uint32_t session);
+
+/**
+ * ptp_closesession:
+ * params:      PTPParams*
+ *
+ * Closes session.
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+#define ptp_closesession(params) ptp_generic_no_data(params,PTP_OC_CloseSession,0)
+/**
+ * ptp_resetdevice:
+ * params:      PTPParams*
+ *              
+ * Uses the built-in function to reset the device
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ */
+#define ptp_resetdevice(params) ptp_generic_no_data(params,PTP_OC_ResetDevice,0)
+
+uint16_t ptp_getstorageids	(PTPParams* params, PTPStorageIDs* storageids);
+uint16_t ptp_getstorageinfo 	(PTPParams* params, uint32_t storageid,
+				PTPStorageInfo* storageinfo);
+/**
+ * ptp_formatstore:
+ * params:      PTPParams*
+ *              storageid               - StorageID
+ *
+ * Formats the storage on the device.
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+#define ptp_formatstore(params,storageid) ptp_generic_no_data(params,PTP_OC_FormatStore,1,storageid)
+
+uint16_t ptp_getobjecthandles 	(PTPParams* params, uint32_t storage,
+				uint32_t objectformatcode,
+				uint32_t associationOH,
+				PTPObjectHandles* objecthandles);
+
+uint16_t ptp_getnumobjects 	(PTPParams* params, uint32_t storage,
+				uint32_t objectformatcode,
+				uint32_t associationOH,
+				uint32_t* numobs);
+
+uint16_t ptp_getobjectinfo	(PTPParams *params, uint32_t handle,
+				PTPObjectInfo* objectinfo);
+
+uint16_t ptp_getobject		(PTPParams *params, uint32_t handle,
+				unsigned char** object);
+uint16_t ptp_getobject_tofd     (PTPParams* params, uint32_t handle, int fd);
+uint16_t ptp_getobject_to_handler (PTPParams* params, uint32_t handle, PTPDataHandler*);
+uint16_t ptp_getpartialobject	(PTPParams* params, uint32_t handle, uint32_t offset,
+				uint32_t maxbytes, unsigned char** object);
+uint16_t ptp_getthumb		(PTPParams *params, uint32_t handle,
+				unsigned char** object);
+
+uint16_t ptp_deleteobject	(PTPParams* params, uint32_t handle,
+				uint32_t ofc);
+
+uint16_t ptp_sendobjectinfo	(PTPParams* params, uint32_t* store,
+				uint32_t* parenthandle, uint32_t* handle,
+				PTPObjectInfo* objectinfo);
+/**
+ * ptp_setobjectprotection:
+ * params:      PTPParams*
+ *              uint16_t newprot        - object protection flag
+ *              
+ * Set protection of object.
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ */
+#define ptp_setobjectprotection(params,oid,newprot) ptp_generic_no_data(params,PTP_OC_SetObjectProtection,2,oid,newprot)
+uint16_t ptp_sendobject		(PTPParams* params, unsigned char* object,
+				 uint32_t size);
+uint16_t ptp_sendobject_fromfd  (PTPParams* params, int fd, uint32_t size);
+uint16_t ptp_sendobject_from_handler  (PTPParams* params, PTPDataHandler*, uint32_t size);
+/**
+ * ptp_initiatecapture:
+ * params:      PTPParams*
+ *              storageid               - destination StorageID on Responder
+ *              ofc                     - object format code
+ * 
+ * Causes device to initiate the capture of one or more new data objects
+ * according to its current device properties, storing the data into store
+ * indicated by storageid. If storageid is 0x00000000, the object(s) will
+ * be stored in a store that is determined by the capturing device.
+ * The capturing of new data objects is an asynchronous operation.
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+#define ptp_initiatecapture(params,storageid,ofc) ptp_generic_no_data(params,PTP_OC_InitiateCapture,2,storageid,ofc)
+
+uint16_t ptp_getdevicepropdesc	(PTPParams* params, uint16_t propcode,
+				PTPDevicePropDesc *devicepropertydesc);
+uint16_t ptp_getdevicepropvalue	(PTPParams* params, uint16_t propcode,
+				PTPPropertyValue* value, uint16_t datatype);
+uint16_t ptp_setdevicepropvalue (PTPParams* params, uint16_t propcode,
+                        	PTPPropertyValue* value, uint16_t datatype);
+
+
+uint16_t ptp_check_event (PTPParams *params);
+int ptp_get_one_event (PTPParams *params, PTPContainer *evt);
+
+/* Microsoft MTP extensions */
+uint16_t ptp_mtp_getobjectpropdesc (PTPParams* params, uint16_t opc, uint16_t ofc,
+				PTPObjectPropDesc *objectpropertydesc);
+uint16_t ptp_mtp_getobjectpropvalue (PTPParams* params, uint32_t oid, uint16_t opc, 
+				PTPPropertyValue *value, uint16_t datatype);
+uint16_t ptp_mtp_setobjectpropvalue (PTPParams* params, uint32_t oid, uint16_t opc,
+				PTPPropertyValue *value, uint16_t datatype);
+uint16_t ptp_mtp_getobjectreferences (PTPParams* params, uint32_t handle, uint32_t** ohArray, uint32_t* arraylen);
+uint16_t ptp_mtp_setobjectreferences (PTPParams* params, uint32_t handle, uint32_t* ohArray, uint32_t arraylen);
+uint16_t ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPProperties **props, int *nrofprops);
+uint16_t ptp_mtp_sendobjectproplist (PTPParams* params, uint32_t* store, uint32_t* parenthandle, uint32_t* handle,
+				     uint16_t objecttype, uint64_t objectsize, MTPProperties *props, int nrofprops);
+uint16_t ptp_mtp_setobjectproplist (PTPParams* params, MTPProperties *props, int nrofprops);
+
+/* Eastman Kodak extensions */
+uint16_t ptp_ek_9007 (PTPParams* params, unsigned char **serial, unsigned int *size);
+uint16_t ptp_ek_9009 (PTPParams* params, uint32_t*, uint32_t*);
+uint16_t ptp_ek_900c (PTPParams* params, unsigned char **serial, unsigned int *size);
+uint16_t ptp_ek_getserial (PTPParams* params, unsigned char **serial, unsigned int *size);
+uint16_t ptp_ek_setserial (PTPParams* params, unsigned char *serial, unsigned int size);
+uint16_t ptp_ek_settext (PTPParams* params, PTPEKTextParams *text);
+uint16_t ptp_ek_sendfileobjectinfo (PTPParams* params, uint32_t* store,
+				uint32_t* parenthandle, uint32_t* handle,
+				PTPObjectInfo* objectinfo);
+uint16_t ptp_ek_sendfileobject	(PTPParams* params, unsigned char* object,
+				uint32_t size);
+uint16_t ptp_ek_sendfileobject_from_handler	(PTPParams* params, PTPDataHandler*,
+				uint32_t size);
+
+/* Canon PTP extensions */
+#define ptp_canon_9012(params) ptp_generic_no_data(params,0x9012,0)
+uint16_t ptp_canon_gettreeinfo (PTPParams* params, uint32_t* out);
+uint16_t ptp_canon_gettreesize (PTPParams* params, PTPCanon_directtransfer_entry**, unsigned int*cnt);
+uint16_t ptp_canon_getpartialobjectinfo (PTPParams* params, uint32_t handle,
+				uint32_t p2, uint32_t* size, uint32_t* rp2);
+
+uint16_t ptp_canon_get_mac_address (PTPParams* params, unsigned char **mac);
+/**
+ * ptp_canon_startshootingmode:
+ * params:      PTPParams*
+ * 
+ * Starts shooting session. It emits a StorageInfoChanged
+ * event via the interrupt pipe and pushes the StorageInfoChanged
+ * and CANON_CameraModeChange events onto the event stack
+ * (see operation PTP_OC_CANON_CheckEvent).
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_startshootingmode(params) ptp_generic_no_data(params,PTP_OC_CANON_InitiateReleaseControl,0)
+/**
+ * ptp_canon_endshootingmode:
+ * params:      PTPParams*
+ * 
+ * This operation is observed after pressing the Disconnect 
+ * button on the Remote Capture app. It emits a StorageInfoChanged 
+ * event via the interrupt pipe and pushes the StorageInfoChanged
+ * and CANON_CameraModeChange events onto the event stack
+ * (see operation PTP_OC_CANON_CheckEvent).
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_endshootingmode(params) ptp_generic_no_data(params,PTP_OC_CANON_TerminateReleaseControl,0)
+/**
+ * ptp_canon_viewfinderon:
+ * params:      PTPParams*
+ * 
+ * Prior to start reading viewfinder images, one  must call this operation.
+ * Supposedly, this operation affects the value of the CANON_ViewfinderMode
+ * property.
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_viewfinderon(params) ptp_generic_no_data(params,PTP_OC_CANON_ViewfinderOn,0)
+/**
+ * ptp_canon_viewfinderoff:
+ * params:      PTPParams*
+ * 
+ * Before changing the shooting mode, or when one doesn't need to read
+ * viewfinder images any more, one must call this operation.
+ * Supposedly, this operation affects the value of the CANON_ViewfinderMode
+ * property.
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_viewfinderoff(params) ptp_generic_no_data(params,PTP_OC_CANON_ViewfinderOff,0)
+/**
+ * ptp_canon_reset_aeafawb:
+ * params:      PTPParams*
+ *              uint32_t flags  - what shall be reset.
+ *                      1 - autoexposure
+ *                      2 - autofocus
+ *                      4 - autowhitebalance
+ * 
+ * Called "Reset AeAfAwb" (auto exposure, focus, white balance)
+ *
+ * Return values: Some PTP_RC_* code.
+ **/
+#define PTP_CANON_RESET_AE	0x1
+#define PTP_CANON_RESET_AF	0x2
+#define PTP_CANON_RESET_AWB	0x4
+#define ptp_canon_reset_aeafawb(params,flags) ptp_generic_no_data(params,PTP_OC_CANON_DoAeAfAwb,1,flags)
+uint16_t ptp_canon_checkevent (PTPParams* params, 
+				PTPContainer* event, int* isevent);
+/**
+ * ptp_canon_focuslock:
+ *
+ * This operation locks the focus. It is followed by the CANON_GetChanges(?)
+ * operation in the log. 
+ * It affects the CANON_MacroMode property. 
+ *
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_focuslock(params) ptp_generic_no_data(params,PTP_OC_CANON_FocusLock,0)
+/**
+ * ptp_canon_focusunlock:
+ *
+ * This operation unlocks the focus. It is followed by the CANON_GetChanges(?)
+ * operation in the log. 
+ * It sets the CANON_MacroMode property value to 1 (where it occurs in the log).
+ * 
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_focusunlock(params) ptp_generic_no_data(params,PTP_OC_CANON_FocusUnlock,0)
+/**
+ * ptp_canon_keepdeviceon:
+ *
+ * This operation sends a "ping" style message to the camera.
+ * 
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_keepdeviceon(params) ptp_generic_no_data(params,PTP_OC_CANON_KeepDeviceOn,0)
+/**
+ * ptp_canon_eos_keepdeviceon:
+ *
+ * This operation sends a "ping" style message to the camera.
+ * 
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_keepdeviceon(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_KeepDeviceOn,0)
+/**
+ * ptp_canon_initiatecaptureinmemory:
+ * 
+ * This operation starts the image capture according to the current camera
+ * settings. When the capture has happened, the camera emits a CaptureComplete
+ * event via the interrupt pipe and pushes the CANON_RequestObjectTransfer,
+ * CANON_DeviceInfoChanged and CaptureComplete events onto the event stack
+ * (see operation CANON_CheckEvent). From the CANON_RequestObjectTransfer
+ * event's parameter one can learn the just captured image's ObjectHandle.
+ * The image is stored in the camera's own RAM.
+ * On the next capture the image will be overwritten!
+ *
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_initiatecaptureinmemory(params) ptp_generic_no_data(params,PTP_OC_CANON_InitiateCaptureInMemory,0)
+/**
+ * ptp_canon_eos_requestdevicepropvalue:
+ *
+ * This operation sends a "ping" style message to the camera.
+ * 
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_requestdevicepropvalue(params,prop) ptp_generic_no_data(params,PTP_OC_CANON_EOS_RequestDevicePropValue,1,prop)
+/**
+ * ptp_canon_eos_capture:
+ * 
+ * This starts a EOS400D style capture. You have to use the
+ * 0x9116 command to poll for its completion.
+ * The image is saved on the CF Card currently.
+ *
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_capture(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_RemoteRelease,0)
+uint16_t ptp_canon_eos_getevent (PTPParams* params, PTPCanon_changes_entry **entries, int *nrofentries);
+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);
+uint16_t ptp_canon_getviewfinderimage (PTPParams* params, unsigned char** image,
+				uint32_t* size);
+uint16_t ptp_canon_getchanges (PTPParams* params, uint16_t** props,
+				uint32_t* propnum); 
+uint16_t ptp_canon_getobjectinfo (PTPParams* params, uint32_t store,
+				uint32_t p2, uint32_t parenthandle,
+				uint32_t handle, 
+				PTPCANONFolderEntry** entries,
+				uint32_t* entnum);
+uint16_t ptp_canon_eos_getdeviceinfo (PTPParams* params, PTPCanonEOSDeviceInfo*di);
+/**
+ * ptp_canon_eos_setuilock:
+ *
+ * This command sets UI lock
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_setuilock(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_SetUILock,0)
+/**
+ * ptp_canon_eos_resetuilock:
+ *
+ * This command sets UI lock
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_resetuilock(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_ResetUILock,0)
+/**
+ * ptp_canon_eos_start_viewfinder:
+ *
+ * This command starts Viewfinder mode of newer Canon DSLRs.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_start_viewfinder(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_InitiateViewfinder,0)
+/**
+ * ptp_canon_eos_end_viewfinder:
+ *
+ * This command ends Viewfinder mode of newer Canon DSLRs.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_eos_end_viewfinder(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_TerminateViewfinder,0)
+uint16_t ptp_canon_eos_get_viewfinder_image (PTPParams* params, unsigned char **data, unsigned int *size);
+uint16_t ptp_canon_get_objecthandle_by_name (PTPParams* params, char* name, uint32_t* objectid);
+uint16_t ptp_canon_get_directory (PTPParams* params, PTPObjectHandles *handles, PTPObjectInfo **oinfos, uint32_t **flags);
+/**
+ * ptp_canon_setobjectarchive:
+ *
+ * params:      PTPParams*
+ *              uint32_t        objectid
+ *              uint32_t        flags
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_canon_setobjectarchive(params,oid,flags) ptp_generic_no_data(params,PTP_OC_CANON_SetObjectArchive,2,oid,flags)
+uint16_t ptp_canon_get_customize_data (PTPParams* params, uint32_t themenr,
+				unsigned char **data, unsigned int *size);
+uint16_t ptp_canon_getpairinginfo (PTPParams* params, uint32_t nr, unsigned char**, unsigned int*);
+
+uint16_t ptp_canon_eos_getstorageids (PTPParams* params, PTPStorageIDs* storageids);
+uint16_t ptp_canon_eos_getstorageinfo (PTPParams* params, uint32_t p1);
+uint16_t ptp_canon_eos_getpartialobject (PTPParams* params, uint32_t oid, uint32_t off, uint32_t xsize, unsigned char**data);
+uint16_t ptp_canon_eos_setdevicepropvalueex (PTPParams* params, unsigned char* data, unsigned int size);
+#define ptp_canon_eos_setremotemode(params,p1) ptp_generic_no_data(params,PTP_OC_CANON_EOS_SetRemoteMode,1,p1)
+#define ptp_canon_eos_seteventmode(params,p1) ptp_generic_no_data(params,PTP_OC_CANON_EOS_SetEventMode,1,p1)
+/**
+ * ptp_canon_eos_transfercomplete:
+ * 
+ * This ends a direct object transfer from an EOS camera.
+ *
+ * params:      PTPParams*
+ *              oid             Object ID
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ */
+#define ptp_canon_eos_transfercomplete(params,oid) ptp_generic_no_data(params,PTP_OC_CANON_EOS_TransferComplete,1,oid)
+/* inHDD = %d, inLength =%d, inReset = %d */
+#define ptp_canon_eos_pchddcapacity(params,p1,p2,p3) ptp_generic_no_data(params,PTP_OC_CANON_EOS_PCHDDCapacity,3,p1,p2,p3)
+#define ptp_canon_eos_bulbstart(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_BulbStart,1)
+#define ptp_canon_eos_bulbend(params) ptp_generic_no_data(params,PTP_OC_CANON_EOS_BulbEnd,1)
+uint16_t ptp_canon_eos_getdevicepropdesc (PTPParams* params, uint16_t propcode,
+				PTPDevicePropDesc *devicepropertydesc);
+uint16_t ptp_canon_eos_setdevicepropvalue (PTPParams* params, uint16_t propcode,
+                        	PTPPropertyValue* value, uint16_t datatype);
+uint16_t ptp_nikon_get_vendorpropcodes (PTPParams* params, uint16_t **props, unsigned int *size);
+uint16_t ptp_nikon_curve_download (PTPParams* params, 
+				unsigned char **data, unsigned int *size);
+uint16_t ptp_nikon_getptpipinfo (PTPParams* params, unsigned char **data, unsigned int *size);
+uint16_t ptp_nikon_getwifiprofilelist (PTPParams* params);
+uint16_t ptp_nikon_writewifiprofile (PTPParams* params, PTPNIKONWifiProfile* profile);
+/**
+ * ptp_nikon_deletewifiprofile:
+ *
+ * This command deletes a wifi profile.
+ *  
+ * params:      PTPParams*
+ *      unsigned int profilenr  - profile number
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_deletewifiprofile(params,profilenr) ptp_generic_no_data(params,PTP_OC_NIKON_DeleteProfile,1,profilenr)
+/**
+ * ptp_nikon_setcontrolmode:
+ *
+ * This command can switch the camera to full PC control mode.
+ *  
+ * params:      PTPParams*
+ *      uint32_t mode - mode
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_setcontrolmode(params,mode) ptp_generic_no_data(params,PTP_OC_NIKON_SetControlMode,1,mode)
+/**
+ * ptp_nikon_afdrive:
+ *
+ * This command runs (drives) the lens autofocus.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_afdrive(params) ptp_generic_no_data(params,PTP_OC_NIKON_AfDrive,0)
+/**
+ * ptp_nikon_mfdrive:
+ *
+ * This command runs (drives) the lens autofocus.
+ *  
+ * params:      PTPParams*
+ * flag:        0x1 for (no limit - closest), 0x2 for (closest - no limit)
+ * amount:      amount of steps
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_mfdrive(params,flag,amount) ptp_generic_no_data(params,PTP_OC_NIKON_MfDrive,2,flag,amount)
+/**
+ * ptp_nikon_capture:
+ *
+ * This command captures a picture on the Nikon.
+ *  
+ * params:      PTPParams*
+ *      uint32_t x - unknown parameter. seen to be -1.
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_capture(params,x) ptp_generic_no_data(params,PTP_OC_NIKON_Capture,1,x)
+/**
+ * ptp_nikon_capture_sdram:
+ *
+ * This command captures a picture on the Nikon.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_capture_sdram(params) ptp_generic_no_data(params,PTP_OC_NIKON_AfCaptureSDRAM,0)
+/**
+ * ptp_nikon_start_liveview:
+ *
+ * This command starts LiveView mode of newer Nikons DSLRs.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_start_liveview(params) ptp_generic_no_data(params,PTP_OC_NIKON_StartLiveView,0)
+uint16_t ptp_nikon_get_liveview_image (PTPParams* params, unsigned char**,unsigned int*);
+uint16_t ptp_nikon_get_preview_image (PTPParams* params, unsigned char**, unsigned int*, uint32_t*);
+/**
+ * ptp_nikon_end_liveview:
+ *
+ * This command ends LiveView mode of newer Nikons DSLRs.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_end_liveview(params) ptp_generic_no_data(params,PTP_OC_NIKON_EndLiveView,0)
+uint16_t ptp_nikon_check_event (PTPParams* params, PTPContainer **evt, int *evtcnt);
+uint16_t ptp_nikon_getfileinfoinblock (PTPParams* params, uint32_t p1, uint32_t p2, uint32_t p3,
+					unsigned char **data, unsigned int *size);
+/**
+ * ptp_nikon_device_ready:
+ *
+ * This command checks if the device is ready. Used after
+ * a capture.
+ *  
+ * params:      PTPParams*
+ *
+ * Return values: Some PTP_RC_* code.
+ *
+ **/
+#define ptp_nikon_device_ready(params) ptp_generic_no_data (params, PTP_OC_NIKON_DeviceReady, 0)
+uint16_t ptp_mtp_getobjectpropssupported (PTPParams* params, uint16_t ofc, uint32_t *propnum, uint16_t **props);
+
+/* Non PTP protocol functions */
+int ptp_operation_issupported	(PTPParams* params, uint16_t operation);
+int ptp_event_issupported	(PTPParams* params, uint16_t event);
+int ptp_property_issupported	(PTPParams* params, uint16_t property);
+
+void ptp_free_devicepropdesc	(PTPDevicePropDesc* dpd);
+void ptp_free_devicepropvalue	(uint16_t dt, PTPPropertyValue* dpd);
+void ptp_free_objectpropdesc	(PTPObjectPropDesc* dpd);
+void ptp_free_params		(PTPParams *params);
+void ptp_free_objectinfo	(PTPObjectInfo *oi);
+void ptp_free_object		(PTPObject *oi);
+
+void ptp_perror			(PTPParams* params, uint16_t error);
+
+const char*
+ptp_get_property_description(PTPParams* params, uint16_t dpc);
+
+int
+ptp_render_property_value(PTPParams* params, uint16_t dpc,
+                          PTPDevicePropDesc *dpd, int length, char *out);
+int ptp_render_ofc(PTPParams* params, uint16_t ofc, int spaceleft, char *txt);
+int ptp_render_opcode(PTPParams* params, uint16_t opcode, int spaceleft, char *txt);
+int ptp_render_mtp_propname(uint16_t propid, int spaceleft, char *txt);
+MTPProperties *ptp_get_new_object_prop_entry(MTPProperties **props, int *nrofprops);
+void ptp_destroy_object_prop(MTPProperties *prop);
+void ptp_destroy_object_prop_list(MTPProperties *props, int nrofprops);
+MTPProperties *ptp_find_object_prop_in_cache(PTPParams *params, uint32_t const handle, uint32_t const attribute_id);
+void ptp_remove_object_from_cache(PTPParams *params, uint32_t handle);
+uint16_t ptp_add_object_to_cache(PTPParams *params, uint32_t handle);
+uint16_t ptp_object_want (PTPParams *, uint32_t handle, int want, PTPObject**retob);
+void ptp_objects_sort (PTPParams *);
+uint16_t ptp_object_find (PTPParams *params, uint32_t handle, PTPObject **retob);
+uint16_t ptp_object_find_or_insert (PTPParams *params, uint32_t handle, PTPObject **retob);
+/* ptpip.c */
+void ptp_nikon_getptpipguid (unsigned char* guid);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PTP_H__ */
diff --git a/src/unicode.c b/src/unicode.c
new file mode 100644
index 0000000..9b17c2b
--- /dev/null
+++ b/src/unicode.c
@@ -0,0 +1,173 @@
+/**
+ * \file unicode.c
+ *
+ * This file contains general Unicode string manipulation functions.
+ * It mainly consist of functions for converting between UCS-2 (used on
+ * the devices) and UTF-8 (used by several applications).
+ *
+ * For a deeper understanding of Unicode encoding formats see the
+ * Wikipedia entries for
+ * <a href="http://en.wikipedia.org/wiki/UTF-16/UCS-2">UTF-16/UCS-2</a>
+ * and <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a>.
+ *
+ * Copyright (C) 2005-2009 Linus Walleij <triad@df.lth.se>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_ICONV
+#include "iconv.h"
+#else
+#error "libmtp unicode.c needs fixing to work without iconv()!"
+#endif
+#include "libmtp.h"
+#include "unicode.h"
+#include "util.h"
+#include "ptp.h"
+
+/**
+ * The size of the buffer (in characters) used for creating string copies.
+ */
+#define STRING_BUFFER_LENGTH 1024
+
+/**
+ * Gets the length (in characters, not bytes) of a unicode
+ * UCS-2 string, eg a string which physically is 0x00 0x41 0x00 0x00
+ * will return a value of 1.
+ *
+ * @param unicstr a UCS-2 Unicode string
+ * @return the length of the string, in number of characters. If you
+ *         want to know the length in bytes, multiply this by two and
+ *         add two (for zero terminator).
+ */
+int ucs2_strlen(uint16_t const * const unicstr)
+{
+  int length;
+
+  /* Unicode strings are terminated with 2 * 0x00 */
+  for(length = 0; unicstr[length] != 0x0000U; length ++);
+  return length;
+}
+
+/**
+ * Converts a big-endian UTF-16 2-byte string
+ * to a UTF-8 string. Actually just a UCS-2 internal conversion
+ * routine that strips off the BOM if there is one.
+ *
+ * @param device a pointer to the current device.
+ * @param unicstr the UTF-16 unicode string to convert
+ * @return a UTF-8 string.
+ */
+char *utf16_to_utf8(LIBMTP_mtpdevice_t *device, const uint16_t *unicstr)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  char *stringp = (char *) unicstr;
+  char loclstr[STRING_BUFFER_LENGTH*3+1]; // UTF-8 encoding is max 3 bytes per UCS2 char.
+  char *locp = loclstr;
+  size_t nconv;
+  size_t convlen = (ucs2_strlen(unicstr)+1) * sizeof(uint16_t); // UCS-2 is 16 bit wide, include terminator
+  size_t convmax = STRING_BUFFER_LENGTH*3;
+
+  loclstr[0]='\0';
+  /* Do the conversion.  */
+  nconv = iconv(params->cd_ucs2_to_locale, &stringp, &convlen, &locp, &convmax);
+  if (nconv == (size_t) -1) {
+    // Return partial string anyway.
+    *locp = '\0';
+  }
+  loclstr[STRING_BUFFER_LENGTH*3] = '\0';
+  // Strip off any BOM, it's totally useless...
+  if ((uint8_t) loclstr[0] == 0xEFU && (uint8_t) loclstr[1] == 0xBBU && (uint8_t) loclstr[2] == 0xBFU) {
+    return strdup(loclstr+3);
+  }
+  return strdup(loclstr);
+}
+
+/**
+ * Converts a UTF-8 string to a big-endian UTF-16 2-byte string
+ * Actually just a UCS-2 internal conversion.
+ *
+ * @param device a pointer to the current device.
+ * @param localstr the UTF-8 unicode string to convert
+ * @return a UTF-16 string.
+ */
+uint16_t *utf8_to_utf16(LIBMTP_mtpdevice_t *device, const char *localstr)
+{
+  PTPParams *params = (PTPParams *) device->params;
+  char *stringp = (char *) localstr; // cast away "const"
+  char unicstr[(STRING_BUFFER_LENGTH+1)*2]; // UCS2 encoding is 2 bytes per UTF-8 char.
+  char *unip = unicstr;
+  size_t nconv = 0;
+  size_t convlen = strlen(localstr)+1; // utf8 bytes, include terminator
+  size_t convmax = STRING_BUFFER_LENGTH*2;
+
+  unicstr[0]='\0';
+  unicstr[1]='\0';
+
+  /* Do the conversion.  */
+  nconv = iconv(params->cd_locale_to_ucs2, &stringp, &convlen, &unip, &convmax);
+
+  if (nconv == (size_t) -1) {
+    // Return partial string anyway.
+    unip[0] = '\0';
+    unip[1] = '\0';
+  }
+  // make sure the string is null terminated
+  unicstr[STRING_BUFFER_LENGTH*2] = '\0';
+  unicstr[STRING_BUFFER_LENGTH*2+1] = '\0';
+
+  // allocate the string to be returned
+  // Note: can't use strdup since every other byte is a null byte
+  int ret_len = ucs2_strlen((uint16_t*)unicstr)*sizeof(uint16_t)+2;
+  uint16_t* ret = malloc(ret_len);
+  memcpy(ret,unicstr,(size_t)ret_len);
+  return ret;
+}
+
+/**
+ * This helper function simply removes any consecutive chars
+ * > 0x7F and replace then with an underscore. In UTF-8
+ * consequtive chars > 0x7F represent one single character so
+ * it has to be done like this (and it's elegant). It will only
+ * shrink the string in size so no copying is needed.
+ */
+void strip_7bit_from_utf8(char *str)
+{
+  int i,j,k;
+  i = 0;
+  j = 0;
+  k = strlen(str);
+  while (i < k) {
+    if ((uint8_t) str[i] > 0x7FU) {
+      str[j] = '_';
+      i++;
+      // Skip over any consequtive > 0x7F chars.
+      while((uint8_t) str[i] > 0x7FU) {
+	i++;
+      }
+    } else {
+      str[j] = str[i];
+      i++;
+    }
+    j++;
+  }
+  // Terminate stripped string...
+  str[j] = '\0';
+}
diff --git a/src/unicode.h b/src/unicode.h
new file mode 100644
index 0000000..f612c32
--- /dev/null
+++ b/src/unicode.h
@@ -0,0 +1,40 @@
+/**
+ * \file unicode.h
+ *
+ * This file contains general Unicode string manipulation functions.
+ * It mainly consist of functions for converting between UCS-2 (used on
+ * the devices) and UTF-8 (used by several applications).
+ *
+ * For a deeper understanding of Unicode encoding formats see the
+ * Wikipedia entries for
+ * <a href="http://en.wikipedia.org/wiki/UTF-16/UCS-2">UTF-16/UCS-2</a>
+ * and <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a>.
+ *
+ * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __MTP__UNICODE__H
+#define __MTP__UNICODE__H
+
+int ucs2_strlen(uint16_t const * const);
+char *utf16_to_utf8(LIBMTP_mtpdevice_t*,const uint16_t*);
+uint16_t *utf8_to_utf16(LIBMTP_mtpdevice_t*, const char*);
+void strip_7bit_from_utf8(char *str);
+
+#endif /* __MTP__UNICODE__H */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..79ad79c
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,107 @@
+/**
+ * \file util.c
+ *
+ * This file contains generic utility functions such as can be
+ * used for debugging for example.
+ *
+ * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* MSVC does not have these */
+#ifndef _MSC_VER
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libmtp.h"
+#include "util.h"
+
+/**
+ * This dumps out a number of bytes to a textual, hexadecimal
+ * dump.
+ *
+ * @param f the file to dump to (e.g. stdout or stderr)
+ * @param buf a pointer to the buffer containing the bytes to
+ *            be dumped out in hex
+ * @param n the number of bytes to dump from this buffer
+ */
+void data_dump (FILE *f, void *buf, uint32_t n)
+{
+  unsigned char *bp = (unsigned char *) buf;
+  uint32_t i;
+  
+  for (i = 0; i < n; i++) {
+    fprintf(f, "%02x ", *bp);
+    bp++;
+  }
+  fprintf(f, "\n");
+}
+
+/**
+ * This dumps out a number of bytes to a textual, hexadecimal
+ * dump, and also prints out the string ASCII representation
+ * for each line of bytes. It will also print the memory address
+ * offset from a certain boundry.
+ *
+ * @param f the file to dump to (e.g. stdout or stderr)
+ * @param buf a pointer to the buffer containing the bytes to
+ *            be dumped out in hex
+ * @param n the number of bytes to dump from this buffer
+ * @param dump_boundry the address offset to start at (usually 0)
+ */
+void data_dump_ascii (FILE *f, void *buf, uint32_t n, uint32_t dump_boundry)
+{
+  uint32_t remain = n;
+  uint32_t ln, lc;
+  int i;
+  unsigned char *bp = (unsigned char *) buf;
+  
+  lc = 0;
+  while (remain) {
+    fprintf(f, "\t%04x:", dump_boundry-0x10);
+    
+    ln = ( remain > 16 ) ? 16 : remain;
+    
+    for (i = 0; i < ln; i++) {
+      if ( ! (i%2) ) fprintf(f, " ");
+      fprintf(f, "%02x", bp[16*lc+i]);
+    }
+    
+    if ( ln < 16 ) {
+      int width = ((16-ln)/2)*5 + (2*(ln%2));
+      fprintf(f, "%*.*s", width, width, "");
+    }
+    
+    fprintf(f, "\t");
+    for (i = 0; i < ln; i++) {
+      unsigned char ch= bp[16*lc+i];
+      fprintf(f, "%c", ( ch >= 0x20 && ch <= 0x7e ) ? 
+	      ch : '.');
+    }
+    fprintf(f, "\n");
+    
+    lc++;
+    remain -= ln;
+    dump_boundry += ln;
+  }
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..8066a60
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,29 @@
+/*
+ * \file util.h
+ * Utilityfunctions.
+ *
+ * Copyright (C) 2005-2007 Linus Walleij <triad@df.lth.se>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MTP__UTIL__H
+#define __MTP__UTIL__H
+
+void data_dump(FILE *f, void *buf, uint32_t nbytes);
+void data_dump_ascii (FILE *f, void *buf, uint32_t n, uint32_t dump_boundry);
+
+#endif //__MTP__UTIL__H