/*
 *   elektron.c
 *   Copyright (C) 2019 David García Goñi <dagargo@gmail.com>
 *
 *   This file is part of Elektroid.
 *
 *   Elektroid is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Elektroid is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Elektroid. If not, see <http://www.gnu.org/licenses/>.
 */

#include <glib/gi18n.h>
#include <stdio.h>
#include <math.h>
#include <zlib.h>
#include "common.h"
#include "elektron.h"
#include "elektron_pkg.h"
#include "sample_ops.h"
#include "../config.h"

#define DEVICES_FILE "/elektron/devices.json"

#define DEV_TAG_ID "id"
#define DEV_TAG_NAME "name"
#define DEV_TAG_FILESYSTEMS "filesystems"
#define DEV_TAG_STORAGE "storage"

#define RAM_OP_DEF_DIR "/incoming"

static const gchar *FS_TYPE_NAMES[] = { "+Drive", "RAM", NULL };

static const gchar *FS_RAW_ANY_EXTS[] = { "raw", NULL };
static const gchar *FS_DATA_ANY_EXTS[] = { "data", NULL };

#define DATA_TRANSF_BLOCK_BYTES 0x2000
#define OS_TRANSF_BLOCK_BYTES 0x800
#define MAX_ZIP_SIZE (128 * 1024 * 1024)

#define FS_DATA_PRJ_PREFIX "/projects"
#define FS_DATA_SND_PREFIX "/soundbanks"
#define FS_DATA_PST_PREFIX "/presets"
#define FS_DATA_SAMPLES_PREFIX "/samples"
#define FS_SMPLRW_START_POS 5
#define FS_DATA_START_POS 18
#define FS_RAM_SLOTS_START_POS 5
#define FS_SAMPLES_SIZE_POS_W 21
#define FS_SAMPLES_LAST_FRAME_POS_W 33
#define FS_SAMPLES_PAD_RES 22

#define ELEKTRON_NAME_MAX_LEN 32
#define ELEKTRON_DATA_SAMPLE_MAX_LEN 16

#define ELEKTRON_SAMPLE_INFO_PAD_I32_LEN 10
#define ELEKTRON_LOOP_TYPE_FWD 0
#define ELEKTRON_LOOP_TYPE_NO 0x7f

#define PROJECT_SLOTS 128
#define SOUND_SLOTS 256
#define SAMPLE_SLOTS 64

#define AH_FX_SLOTS 512
#define AH_SLOTS 128

#define ELEKTRON_RAM_SLOT_USED "used"

#define ELEKTRON_SYNTAKT_MAX_SAMPLE_TIME_S 5
#define ELEKTRON_SYNTAKT_MAX_SAMPLE_FRAMES (ELEKTRON_SAMPLE_RATE * ELEKTRON_SYNTAKT_MAX_SAMPLE_TIME_S)

#define ELEKTRON_MC_SOUND_EXTENSION "mc-snd"
#define ELEKTRON_MC_SOUND_EXTENSION_WITH_DOT ("." ELEKTRON_MC_SOUND_EXTENSION)

struct elektron_sample_header
{
  guint8 type;
  guint8 stereo;		//0: mono, 1: stereo interleaved
  guint8 rsvd0[2];
  guint32 size;			//Bytes
  guint32 rate;
  guint32 loop_start;
  guint32 loop_end;
  guint8 loop_type;		// as in midi sds, 0x00 = forward loop, 0x7F = no loop
  guint8 rsvd1[3];
  guint32 padding[ELEKTRON_SAMPLE_INFO_PAD_I32_LEN];
};

enum elektron_iterator_mode
{
  ITER_MODE_SAMPLE,
  ITER_MODE_RAW,
  ITER_MODE_DATA,
  ITER_MODE_DATA_SND,
  ITER_MODE_RAM_SLOTS,
  ITER_MODE_RAM_TRACKS
};

struct elektron_iterator_data
{
  GByteArray *msg;
  guint32 pos;
  guint32 hash;
  guint16 operations;
  guint8 has_valid_data;
  guint8 has_metadata;
  enum elektron_iterator_mode mode;
  gint32 max_slots;
  struct backend *backend;
  gboolean load_metadata;
};

struct __attribute__((packed)) elektron_data_header
{
  guint32 magic_head;		// 0xac11d303
  guint8 header_version;
  guint16 family_id;		// 8 for Syntakt
  guint16 device_id;		// 13 for Syntakt
  guint32 os_version;		// OS release number
  guint32 file_type;		// 8 for Syntakt samples
  gint32 file_version;
  gint32 file_index;
  guint32 payload_size;		// uncompressed
  guint8 is_compressed;
  guint8 footer_size;		// Size of footer
};

struct __attribute__((packed)) elektron_data_sample_slot_header
{
  guint32 magic_head;		// 0x53414d50
  guint8 version;		// Should be 0
  guint8 pad[3];		// ignore
  guint32 length;
  gchar name[ELEKTRON_DATA_SAMPLE_MAX_LEN];
  guint32 rsvd32[9];		// ignore
};

struct __attribute__((packed)) elektron_data_footer
{
  guint32 hash;
  guint32 content_size;
  guint32 magic_tail;		// 0xaaa1daaa
};

typedef GByteArray *(*elektron_msg_id_func) (guint);

typedef GByteArray *(*elektron_msg_id_len_func) (guint, guint);

typedef GByteArray *(*elektron_msg_path_func) (const gchar *);

typedef GByteArray *(*elektron_msg_path_len_func) (const gchar *, guint);

typedef GByteArray *(*elektron_msg_read_blk_func) (guint, guint, guint);

typedef GByteArray *(*elektron_msg_write_blk_func) (guint, GByteArray *,
						    guint *, guint, void *);

typedef void (*elektron_copy_array) (GByteArray *, GByteArray *);

typedef gint (*elektron_path_func) (struct backend *, const gchar *);

typedef gint (*elektron_src_dst_func) (struct backend *, const gchar *,
				       const gchar *);

static gint elektron_download_data_snd (struct backend *, const gchar *,
					struct idata *,
					struct task_control *);

static gint elektron_download_data_snd_pkg (struct backend *, const gchar *,
					    struct idata *,
					    struct task_control *);
static gint elektron_download_data_prj_pkg (struct backend *, const gchar *,
					    struct idata *,
					    struct task_control *);
static gint elektron_download_data_pst_pkg (struct backend *, const gchar *,
					    struct idata *,
					    struct task_control *);
static gint elektron_download_raw_pst_pkg (struct backend *, const gchar *,
					   struct idata *,
					   struct task_control *);
static gint elektron_upload_data_prj_pkg (struct backend *, const gchar *,
					  struct idata *,
					  struct task_control *);
static gint elektron_upload_data_snd_pkg (struct backend *, const gchar *,
					  struct idata *,
					  struct task_control *);
static gint elektron_upload_data_pst_pkg (struct backend *, const gchar *,
					  struct idata *,
					  struct task_control *);
static gint elektron_upload_raw_pst_pkg (struct backend *, const gchar *,
					 struct idata *,
					 struct task_control *);

static gboolean elektron_sample_file_exists (struct backend *, const gchar *);
static gboolean elektron_raw_file_exists (struct backend *, const gchar *);
const gchar **elektron_get_dev_exts (struct backend *,
				     const struct fs_operations *);

static const guint8 MSG_HEADER[] = { 0xf0, 0, 0x20, 0x3c, 0x10, 0 };

static const guint8 PING_REQUEST[] = { 0x1 };
static const guint8 SOFTWARE_VERSION_REQUEST[] = { 0x2 };
static const guint8 DEVICEUID_REQUEST[] = { 0x3 };
static const guint8 STORAGEINFO_REQUEST[] = { 0x5 };

static const guint8 FS_SAMPLE_READ_DIR_REQUEST[] = { 0x10 };
static const guint8 FS_SAMPLE_CREATE_DIR_REQUEST[] = { 0x11 };
static const guint8 FS_SAMPLE_DELETE_DIR_REQUEST[] = { 0x12 };
static const guint8 FS_SAMPLE_DELETE_FILE_REQUEST[] = { 0x20 };
static const guint8 FS_SAMPLE_RENAME_FILE_REQUEST[] = { 0x21 };
static const guint8 FS_SAMPLE_GET_FILE_INFO_FROM_PATH_REQUEST[] = { 0x22 };	//Returns size and 4 unknown bytes
static const guint8 FS_SAMPLE_GET_FILE_INFO_FROM_HASH_AND_SIZE_REQUEST[] =
  { 0x23, 0, 0, 0, 0, 0, 0, 0, 0 };
static const guint8 FS_SAMPLE_OPEN_FILE_READER_REQUEST[] = { 0x30 };
static const guint8 FS_SAMPLE_CLOSE_FILE_READER_REQUEST[] = { 0x31 };
static const guint8 FS_SAMPLE_READ_FILE_REQUEST[] =
  { 0x32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static const guint8 FS_SAMPLE_OPEN_FILE_WRITER_REQUEST[] =
  { 0x40, 0, 0, 0, 0 };
static const guint8 FS_SAMPLE_CLOSE_FILE_WRITER_REQUEST[] =
  { 0x41, 0, 0, 0, 0, 0, 0, 0, 0 };
static const guint8 FS_SAMPLE_WRITE_FILE_REQUEST[] =
  { 0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

static const guint8 FS_RAW_READ_DIR_REQUEST[] = { 0x14 };
static const guint8 FS_RAW_CREATE_DIR_REQUEST[] = { 0x15 };
static const guint8 FS_RAW_DELETE_DIR_REQUEST[] = { 0x16 };
static const guint8 FS_RAW_DELETE_FILE_REQUEST[] = { 0x24 };
static const guint8 FS_RAW_RENAME_FILE_REQUEST[] = { 0x25 };
static const guint8 FS_RAW_GET_FILE_INFO_FROM_PATH_REQUEST[] = { 0x26 };
static const guint8 FS_RAW_OPEN_FILE_READER_REQUEST[] = { 0x33 };
static const guint8 FS_RAW_CLOSE_FILE_READER_REQUEST[] = { 0x34 };
static const guint8 FS_RAW_READ_FILE_REQUEST[] =
  { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static const guint8 FS_RAW_OPEN_FILE_WRITER_REQUEST[] = { 0x43, 0, 0, 0, 0 };
static const guint8 FS_RAW_CLOSE_FILE_WRITER_REQUEST[] =
  { 0x44, 0, 0, 0, 0, 0, 0, 0, 0 };
static const guint8 FS_RAW_WRITE_FILE_REQUEST[] =
  { 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

static const guint8 DATA_LIST_REQUEST[] = { 0x53 };
static const guint8 DATA_READ_OPEN_REQUEST[] = { 0x54 };
static const guint8 DATA_READ_PARTIAL_REQUEST[] = { 0x55 };
static const guint8 DATA_READ_CLOSE_REQUEST[] = { 0x56 };
static const guint8 DATA_WRITE_OPEN_REQUEST[] = { 0x57 };
static const guint8 DATA_WRITE_PARTIAL_REQUEST[] = { 0x58 };
static const guint8 DATA_WRITE_CLOSE_REQUEST[] = { 0x59 };
static const guint8 DATA_MOVE_REQUEST[] = { 0x5a };
static const guint8 DATA_COPY_REQUEST[] = { 0x5b };
static const guint8 DATA_CLEAR_REQUEST[] = { 0x5c };
static const guint8 DATA_SWAP_REQUEST[] = { 0x5d };

static const guint8 DIGITAKT_RAM_READ_REQUEST[] =
  { 0x17, 0xff, 0xff, 0xff, 0xff };
static const guint8 DIGITAKT_RAM_LOAD_SLOT_REQUEST[] = { 0x19, 0 };
static const guint8 DIGITAKT_RAM_CLEAR_SLOT_REQUEST[] =
  { 0x18, 0, 0, 1, 0, 0 };

static const guint8 DIGITAKT_GET_PATTERN_INFO[] = { 0x6 };	//Unused. It returns 5 32 bit integer. The first one is the pattern tempo * 120. The second one is the global tempo * 120.

// static const guint8 DIGITAKT_GET_FILE_SIZE_HASH_FROM_PATH_REQUEST[] = { 0x28 }; //Unused. Returns size and hash. Similar to FS_SAMPLE_GET_FILE_INFO_FROM_PATH_REQUEST.

static const guint8 OS_UPGRADE_START_REQUEST[] =
  { 0x50, 0, 0, 0, 0, 's', 'y', 's', 'e', 'x', '\0', 1 };
static const guint8 OS_UPGRADE_WRITE_RESPONSE[] =
  { 0x51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

static void
elektron_set_slot (struct item *item)
{
  if (item->id >= 0)
    {
      snprintf (item->slot, ITEM_SLOT_MAX, "%03d", item->id);
    }
  else
    {
      item->slot[0] = 0;
    }
}

static void
elektron_print_smplrw (struct item_iterator *iter,
		       struct backend *backend,
		       const struct fs_operations *fs_ops)
{
  gchar hsize[LABEL_MAX];
  struct elektron_iterator_data *data = iter->data;

  get_human_size (iter->item.size, FALSE, hsize, LABEL_MAX);
  printf ("%c %10s %08x %s\n", iter->item.type,
	  hsize, data->hash, iter->item.name);
}

static void
elektron_print_data (struct item_iterator *iter,
		     struct backend *backend,
		     const struct fs_operations *fs_ops)
{
  gchar hsize[LABEL_MAX];
  struct elektron_iterator_data *data = iter->data;
  gboolean info = (fs_ops->options & FS_OPTION_SHOW_INFO_COLUMN) &&
    *iter->item.object_info;

  get_human_size (iter->item.size, FALSE, hsize, LABEL_MAX);
  printf ("%c %04x %d %d %*s %*s %-*s%s%s%s\n", iter->item.type,
	  data->operations, data->has_valid_data, data->has_metadata,
	  DEFAULT_PRINT_COL_SIZE_LEN, hsize,
	  DEFAULT_PRINT_COL_SLOT_LEN, iter->item.slot,
	  DEFAULT_PRINT_COL_NAME_LEN, iter->item.name,
	  info ? " [ " : "", iter->item.object_info, info ? " ]" : "");
}

static void
elektron_free_iterator_data (void *iter_data)
{
  struct elektron_iterator_data *data = iter_data;
  free_msg (data->msg);
  g_free (data);
}

static inline gchar *
elektron_name_to_utf8 (const gchar *name)
{
  return g_convert (name, -1, "UTF8", "CP1252", NULL, NULL, NULL);
}

static inline void
elektron_item_set_name (struct item *item, const gchar *name)
{
  gchar *aux = elektron_name_to_utf8 (name);
  item_set_name (item, "%s", aux);
  g_free (aux);
}

static inline gchar *
elektron_get_cp1252 (const gchar *i)
{
  gchar *o = g_convert (i, -1, "CP1252", "UTF8", NULL, NULL, NULL);
  if (!o)
    {
      debug_print (0,
		   "Error while converting “%s” to CP1252. Sanitizing...",
		   i);
      o = common_get_sanitized_name (i, NULL, 0);
    }
  return o;
}

static inline guint8
elektron_get_msg_status (const GByteArray *msg)
{
  return msg->data[5];
}

static inline gchar *
elektron_get_msg_string (const GByteArray *msg)
{
  return (gchar *) & msg->data[6];
}

// This adds back the extension "mc-snd" that the device provides.
static gchar *
elektron_mc_add_snd_ext (const gchar *path)
{
  gchar *path_with_ext;
  GString *str = g_string_new (path);
  g_string_append (str, ELEKTRON_MC_SOUND_EXTENSION_WITH_DOT);
  path_with_ext = g_string_free (str, FALSE);
  return path_with_ext;
}

static void
elektron_mc_remove_snd_ext (gchar *name)
{
  name[strlen (name) - strlen (ELEKTRON_MC_SOUND_EXTENSION_WITH_DOT)] = 0;
}

static gint
elektron_next_smplrw_entry (struct item_iterator *iter)
{
  guint32 *data32;
  gchar *name_cp1252;
  struct elektron_iterator_data *data = iter->data;

  if (data->pos == data->msg->len)
    {
      return -ENOENT;
    }
  else
    {
      data32 = (guint32 *) & data->msg->data[data->pos];
      data->hash = g_ntohl (*data32);
      data->pos += sizeof (guint32);

      data32 = (guint32 *) & data->msg->data[data->pos];
      iter->item.size = g_ntohl (*data32);
      data->pos += sizeof (guint32);

      data->pos++;		//write_protected

      iter->item.type = data->msg->data[data->pos];
      data->pos++;

      name_cp1252 = (gchar *) & data->msg->data[data->pos];
      elektron_item_set_name (&iter->item, name_cp1252);
      if (data->mode == ITER_MODE_RAW && iter->item.type == ITEM_TYPE_FILE)
	{
	  elektron_mc_remove_snd_ext (iter->item.name);
	}
      data->pos += strlen (name_cp1252) + 1;

      iter->item.id = -1;
      sample_info_init (&iter->item.sample_info);

      return 0;
    }
}

static gint
elektron_init_iterator (struct backend *backend, struct item_iterator *iter,
			const gchar *dir, GByteArray *msg, iterator_next next,
			enum elektron_iterator_mode mode, guint32 start_pos,
			gint32 max_slots)
{
  struct elektron_iterator_data *data =
    g_malloc (sizeof (struct elektron_iterator_data));

  data->msg = msg;
  data->pos = start_pos;
  data->mode = mode;
  data->max_slots = max_slots;
  data->backend = backend;
  data->load_metadata = TRUE;

  item_iterator_init (iter, dir, data, next, elektron_free_iterator_data);
  iter->item.id = 0;		//This is needed to point to the next item id
  iter->item.type = ITEM_TYPE_NONE;	//This is needed in case the response when reading a directory is empty

  return 0;
}

static GByteArray *
elektron_decode_payload (const GByteArray *src)
{
  GByteArray *dst;
  int i, j, k, dst_len;
  unsigned int shift;

  dst_len = src->len - ceill (src->len / 8.0);
  dst = g_byte_array_sized_new (dst_len);
  dst->len = dst_len;

  for (i = 0, j = 0; i < src->len; i += 8, j += 7)
    {
      shift = 0x40;
      for (k = 0; k < 7 && i + k + 1 < src->len; k++)
	{
	  dst->data[j + k] =
	    src->data[i + k + 1] | (src->data[i] & shift ? 0x80 : 0);
	  shift = shift >> 1;
	}
    }

  return dst;
}

static GByteArray *
elektron_encode_payload (const GByteArray *src)
{
  GByteArray *dst;
  int i, j, k, dst_len;
  unsigned int accum;

  dst_len = src->len + ceill (src->len / 7.0);
  dst = g_byte_array_sized_new (dst_len);
  dst->len = dst_len;

  for (i = 0, j = 0; j < src->len; i += 8, j += 7)
    {
      accum = 0;
      for (k = 0; k < 7; k++)
	{
	  accum = accum << 1;
	  if (j + k < src->len)
	    {
	      if (src->data[j + k] & 0x80)
		{
		  accum |= 1;
		}
	      dst->data[i + k + 1] = src->data[j + k] & 0x7f;
	    }
	}
      dst->data[i] = accum;
    }

  return dst;
}

static GByteArray *
elektron_msg_to_raw (const GByteArray *msg)
{
  GByteArray *encoded = elektron_encode_payload (msg);
  guint total = sizeof (MSG_HEADER) + encoded->len + 1;
  GByteArray *raw = g_byte_array_sized_new (total);

  g_byte_array_append (raw, MSG_HEADER, sizeof (MSG_HEADER));
  g_byte_array_append (raw, encoded->data, encoded->len);
  g_byte_array_append (raw, (guint8 *) "\xf7", 1);
  free_msg (encoded);

  return raw;
}

static gint
elektron_get_smplrw_info_from_msg (GByteArray *info_msg, guint32 *id,
				   guint *size)
{
  switch (elektron_get_msg_status (info_msg))
    {
    case 1:
      *id = g_ntohl (*((guint32 *) & info_msg->data[6]));
      if (size)
	{
	  *size = g_ntohl (*((guint32 *) & info_msg->data[10]));
	}
      return 0;
    case 2:
      return -EINVAL;		// Case for for factory samples.
    default:
      return -EIO;
    }
}

static GByteArray *
elektron_new_msg (const guint8 *data, guint len)
{
  GByteArray *msg = g_byte_array_new ();

  g_byte_array_append (msg, (guchar *) "\0\0\0\0", 4);
  g_byte_array_append (msg, data, len);

  return msg;
}

static GByteArray *
elektron_new_msg_uint8 (const guint8 *data, guint len, guint8 type)
{
  GByteArray *msg = elektron_new_msg (data, len);

  g_byte_array_append (msg, &type, 1);

  return msg;
}

static GByteArray *
elektron_new_msg_path (const guint8 *data, guint len, const gchar *path)
{
  GByteArray *msg;
  gchar *path_cp1252 = elektron_get_cp1252 (path);

  msg = elektron_new_msg (data, len);
  g_byte_array_append (msg, (guchar *) path_cp1252, strlen (path_cp1252) + 1);
  g_free (path_cp1252);

  return msg;
}

static GByteArray *
elektron_new_msg_close_common_read (const guint8 *data, guint len, guint id)
{
  guint32 aux32;
  GByteArray *msg = elektron_new_msg (data, len);

  aux32 = g_htonl (id);
  g_byte_array_append (msg, (guchar *) & aux32, sizeof (guint32));
  return msg;
}

static GByteArray *
elektron_new_msg_close_sample_read (guint id)
{
  return
    elektron_new_msg_close_common_read (FS_SAMPLE_CLOSE_FILE_READER_REQUEST,
					sizeof
					(FS_SAMPLE_CLOSE_FILE_READER_REQUEST),
					id);
}

static GByteArray *
elektron_new_msg_close_raw_read (guint id)
{
  return
    elektron_new_msg_close_common_read (FS_RAW_CLOSE_FILE_READER_REQUEST,
					sizeof
					(FS_RAW_CLOSE_FILE_READER_REQUEST),
					id);
}

static GByteArray *
elektron_new_msg_open_common_write (const guint8 *data, guint len,
				    const gchar *path, guint bytes)
{
  guint32 aux32;
  GByteArray *msg = elektron_new_msg_path (data, len, path);

  aux32 = g_htonl (bytes);
  memcpy (&msg->data[5], &aux32, sizeof (guint32));

  return msg;
}

static GByteArray *
elektron_new_msg_open_sample_write (const gchar *path, guint bytes)
{
  return
    elektron_new_msg_open_common_write (FS_SAMPLE_OPEN_FILE_WRITER_REQUEST,
					sizeof
					(FS_SAMPLE_OPEN_FILE_WRITER_REQUEST),
					path,
					bytes +
					sizeof (struct
						elektron_sample_header));
}

static GByteArray *
elektron_new_msg_open_raw_write (const gchar *path, guint bytes)
{
  return elektron_new_msg_open_common_write (FS_RAW_OPEN_FILE_WRITER_REQUEST,
					     sizeof
					     (FS_RAW_OPEN_FILE_WRITER_REQUEST),
					     path, bytes);
}


static GByteArray *
elektron_new_msg_list (const gchar *path, int32_t start_index,
		       int32_t end_index, gboolean all)
{
  guint32 aux32;
  guint8 aux8;
  GByteArray *msg = elektron_new_msg_path (DATA_LIST_REQUEST,
					   sizeof (DATA_LIST_REQUEST),
					   path);

  aux32 = g_htonl (start_index);
  g_byte_array_append (msg, (guchar *) & aux32, sizeof (guint32));
  aux32 = g_htonl (end_index);
  g_byte_array_append (msg, (guchar *) & aux32, sizeof (guint32));
  aux8 = all;
  g_byte_array_append (msg, (guchar *) & aux8, sizeof (guint8));

  return msg;
}

static GByteArray *
elektron_new_msg_write_sample_blk (guint id, GByteArray *sample,
				   guint *total, guint seq, void *data)
{
  guint32 aux32;
  guint16 aux16, *aux16p;
  int i, consumed, bytes_blk;
  struct sample_info *sample_info = data;
  struct elektron_sample_header elektron_sample_header;
  GByteArray *msg = elektron_new_msg (FS_SAMPLE_WRITE_FILE_REQUEST,
				      sizeof (FS_SAMPLE_WRITE_FILE_REQUEST));


  aux32 = g_htonl (id);
  memcpy (&msg->data[5], &aux32, sizeof (guint32));
  aux32 = g_htonl (DATA_TRANSF_BLOCK_BYTES * seq);
  memcpy (&msg->data[13], &aux32, sizeof (guint32));

  bytes_blk = DATA_TRANSF_BLOCK_BYTES;
  consumed = 0;

  if (seq == 0)
    {
      //See comment in elektron_sample_header struct.
      guint8 loop_type = sample_info->loop_type ? ELEKTRON_LOOP_TYPE_NO :
	ELEKTRON_LOOP_TYPE_FWD;
      elektron_sample_header.type = 0;
      elektron_sample_header.stereo = sample_info->channels - 1;
      memset (&elektron_sample_header.rsvd0, 0, 2);
      elektron_sample_header.size = g_htonl (sample->len);
      elektron_sample_header.rate = g_htonl (ELEKTRON_SAMPLE_RATE);
      elektron_sample_header.loop_start = g_htonl (sample_info->loop_start);
      elektron_sample_header.loop_end = g_htonl (sample_info->loop_end);
      elektron_sample_header.loop_type = loop_type;
      memset (&elektron_sample_header.rsvd1, 0, 3);
      memset (&elektron_sample_header.padding, 0,
	      sizeof (guint32) * ELEKTRON_SAMPLE_INFO_PAD_I32_LEN);

      g_byte_array_append (msg, (guchar *) & elektron_sample_header,
			   sizeof (struct elektron_sample_header));

      consumed = sizeof (struct elektron_sample_header);
      bytes_blk -= consumed;
    }

  i = 0;
  aux16p = (guint16 *) & sample->data[*total];
  while (i < bytes_blk && *total < sample->len)
    {
      aux16 = g_htons (*aux16p);
      g_byte_array_append (msg, (guint8 *) & aux16, sizeof (guint16));
      aux16p++;
      (*total) += sizeof (guint16);
      consumed += sizeof (guint16);
      i += sizeof (guint16);
    }

  aux32 = g_htonl (consumed);
  memcpy (&msg->data[9], &aux32, sizeof (guint32));

  return msg;
}

static GByteArray *
elektron_new_msg_write_raw_blk (guint id, GByteArray *raw, guint *total,
				guint seq, void *data)
{
  gint len;
  guint32 aux32;
  GByteArray *msg = elektron_new_msg (FS_RAW_WRITE_FILE_REQUEST,
				      sizeof (FS_RAW_WRITE_FILE_REQUEST));

  aux32 = g_htonl (id);
  memcpy (&msg->data[5], &aux32, sizeof (guint32));
  aux32 = g_htonl (DATA_TRANSF_BLOCK_BYTES * seq);
  memcpy (&msg->data[13], &aux32, sizeof (guint32));

  len = raw->len - *total;
  len = len > DATA_TRANSF_BLOCK_BYTES ? DATA_TRANSF_BLOCK_BYTES : len;
  g_byte_array_append (msg, &raw->data[*total], len);
  (*total) += len;

  aux32 = g_htonl (len);
  memcpy (&msg->data[9], &aux32, sizeof (guint32));

  return msg;
}

static GByteArray *
elektron_new_msg_close_common_write (const guint8 *data, guint len,
				     guint id, guint bytes)
{
  guint32 aux32;
  GByteArray *msg = elektron_new_msg (data, len);

  aux32 = g_htonl (id);
  memcpy (&msg->data[5], &aux32, sizeof (guint32));
  aux32 = g_htonl (bytes);
  memcpy (&msg->data[9], &aux32, sizeof (guint32));

  return msg;
}

static GByteArray *
elektron_new_msg_close_sample_write (guint id, guint bytes)
{
  return
    elektron_new_msg_close_common_write (FS_SAMPLE_CLOSE_FILE_WRITER_REQUEST,
					 sizeof
					 (FS_SAMPLE_CLOSE_FILE_WRITER_REQUEST),
					 id,
					 bytes +
					 sizeof (struct
						 elektron_sample_header));
}

static GByteArray *
elektron_new_msg_close_raw_write (guint id, guint bytes)
{
  return
    elektron_new_msg_close_common_write (FS_RAW_CLOSE_FILE_WRITER_REQUEST,
					 sizeof
					 (FS_RAW_CLOSE_FILE_WRITER_REQUEST),
					 id, bytes);
}

static GByteArray *
elektron_new_msg_read_common_blk (const guint8 *data, guint len, guint id,
				  guint start, guint size)
{
  guint32 aux;
  GByteArray *msg = elektron_new_msg (data, len);

  aux = g_htonl (id);
  memcpy (&msg->data[5], &aux, sizeof (guint32));
  aux = g_htonl (size);
  memcpy (&msg->data[9], &aux, sizeof (guint32));
  aux = g_htonl (start);
  memcpy (&msg->data[13], &aux, sizeof (guint32));

  return msg;
}

static GByteArray *
elektron_new_msg_read_sample_blk (guint id, guint start, guint size)
{
  return elektron_new_msg_read_common_blk (FS_SAMPLE_READ_FILE_REQUEST,
					   sizeof
					   (FS_SAMPLE_READ_FILE_REQUEST), id,
					   start, size);
}

static GByteArray *
elektron_new_msg_read_raw_blk (guint id, guint start, guint size)
{
  return elektron_new_msg_read_common_blk (FS_RAW_READ_FILE_REQUEST,
					   sizeof (FS_RAW_READ_FILE_REQUEST),
					   id, start, size);
}

static GByteArray *
elektron_raw_to_msg (GByteArray *sysex)
{
  GByteArray *msg;
  GByteArray *payload;
  guint len = sysex->len - sizeof (MSG_HEADER) - 1;

  if (len > 0)
    {
      payload = g_byte_array_sized_new (len);
      g_byte_array_append (payload, &sysex->data[sizeof (MSG_HEADER)], len);
      msg = elektron_decode_payload (payload);
      free_msg (payload);
    }
  else
    {
      msg = NULL;
    }

  return msg;
}

static gint
elektron_tx (struct backend *backend, const GByteArray *msg,
	     struct controllable *controllable)
{
  gint res;
  guint16 aux;
  gchar *text;
  struct sysex_transfer transfer;
  struct elektron_data *data = backend->data;

  aux = g_htons (data->seq);
  memcpy (msg->data, &aux, sizeof (guint16));
  data->seq++;

  sysex_transfer_init_tx (&transfer, elektron_msg_to_raw (msg));

  res = backend_tx_sysex (backend, &transfer, controllable);
  if (!res)
    {
      text = debug_get_hex_msg (msg);
      debug_print (1, "Message sent (%d): %s", msg->len, text);
      g_free (text);
    }

  sysex_transfer_clear (&transfer);

  return res;
}

static GByteArray *
elektron_rx (struct backend *backend, gint timeout,
	     struct controllable *controllable)
{
  gchar *text;
  GByteArray *msg;
  struct sysex_transfer transfer;

  sysex_transfer_init_rx (&transfer, timeout, FALSE);

  while (1)
    {
      if (backend_rx_sysex (backend, &transfer, controllable))
	{
	  return NULL;
	}

      if (transfer.raw->len >= 12
	  && !memcmp (transfer.raw->data, MSG_HEADER, 6))
	{
	  break;
	}

      if (debug_level > 1)
	{
	  text = debug_get_hex_msg (transfer.raw);
	  debug_print (2, "Message skipped (%d): %s", transfer.raw->len,
		       text);
	  g_free (text);
	}
      sysex_transfer_clear (&transfer);
    }

  msg = elektron_raw_to_msg (transfer.raw);
  if (msg)
    {
      text = debug_get_hex_msg (msg);
      debug_print (1, "Message received (%d): %s", msg->len, text);
      g_free (text);
    }

  sysex_transfer_clear (&transfer);

  return msg;
}

//Synchronized

static GByteArray *
elektron_tx_and_rx_timeout (struct backend *backend,
			    GByteArray *tx_msg, gint timeout,
			    struct controllable *controllable)
{
  gssize len;
  guint16 seq;
  GByteArray *rx_msg;
  guint msg_type = tx_msg->data[4] | 0x80;
  struct elektron_data *data = backend->data;
  gint t = timeout < 0 ? BE_SYSEX_TIMEOUT_MS : timeout;

  g_mutex_lock (&backend->mutex);

  seq = data->seq;
  len = elektron_tx (backend, tx_msg, controllable);
  if (len < 0)
    {
      rx_msg = NULL;
      goto cleanup;
    }

  while (1)
    {
      rx_msg = elektron_rx (backend, t, controllable);
      if (!rx_msg)
	{
	  break;
	}

      guint16 exp_seq = g_ntohs (*((guint16 *) & rx_msg->data[2]));
      if (seq != exp_seq)
	{
	  error_print ("Unexpected sequence in response. Skipping...");
	  free_msg (rx_msg);
	  continue;
	}

      if (rx_msg->data[4] != msg_type)
	{
	  error_print ("Illegal message type in response. Skipping...");
	  free_msg (rx_msg);
	  rx_msg = NULL;
	  break;
	}

      break;
    }

cleanup:
  g_mutex_unlock (&backend->mutex);
  free_msg (tx_msg);
  return rx_msg;
}

static GByteArray *
elektron_tx_and_rx (struct backend *backend, GByteArray *tx_msg,
		    struct controllable *controllable)
{
  return elektron_tx_and_rx_timeout (backend, tx_msg, -1, controllable);
}

static enum item_type
elektron_get_path_type (struct backend *backend, const gchar *path,
			fs_init_iter_func init_iter)
{
  gchar *dir, *name;
  enum item_type res;
  struct item_iterator iter;

  if (strcmp (path, "/") == 0)
    {
      return ITEM_TYPE_DIR;
    }

  name = g_path_get_basename (path);
  dir = g_path_get_dirname (path);
  res = ITEM_TYPE_NONE;
  if (!init_iter (backend, &iter, dir, NULL))
    {
      while (!item_iterator_next (&iter))
	{
	  if (strcmp (name, iter.item.name) == 0)
	    {
	      res = iter.item.type;
	      break;
	    }
	}
      item_iterator_free (&iter);
    }

  g_free (name);
  g_free (dir);
  return res;
}

static gint
elektron_read_common_dir (struct backend *backend,
			  struct item_iterator *iter, const gchar *dir,
			  const guint8 msg[], int size,
			  fs_init_iter_func init_iter,
			  enum elektron_iterator_mode mode, guint32 start_pos,
			  fs_file_exists file_exists)
{
  GByteArray *tx_msg, *rx_msg = NULL;
  gboolean is_file = file_exists (backend, dir);

  g_usleep (BE_REST_TIME_US);

  if (is_file)
    {
      return -ENOTDIR;
    }

  tx_msg = elektron_new_msg_path (msg, size, dir);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  if (rx_msg->len == 5 &&
      elektron_get_path_type (backend, dir, init_iter) != ITEM_TYPE_DIR)
    {
      free_msg (rx_msg);
      return -ENOTDIR;
    }

  return elektron_init_iterator (backend, iter, dir, rx_msg,
				 elektron_next_smplrw_entry, mode, start_pos,
				 -1);
}

static gint
elektron_read_samples_dir (struct backend *backend,
			   struct item_iterator *iter, const gchar *dir,
			   const gchar **extensions)
{
  return elektron_read_common_dir (backend, iter, dir,
				   FS_SAMPLE_READ_DIR_REQUEST,
				   sizeof (FS_SAMPLE_READ_DIR_REQUEST),
				   elektron_read_samples_dir,
				   ITER_MODE_SAMPLE, FS_SMPLRW_START_POS,
				   elektron_sample_file_exists);
}

static gint
elektron_read_raw_dir (struct backend *backend, struct item_iterator *iter,
		       const gchar *dir, const gchar **extensions)
{
  return elektron_read_common_dir (backend, iter, dir,
				   FS_RAW_READ_DIR_REQUEST,
				   sizeof (FS_RAW_READ_DIR_REQUEST),
				   elektron_read_raw_dir, ITER_MODE_RAW,
				   FS_SMPLRW_START_POS,
				   elektron_raw_file_exists);
}

static gint
elektron_src_dst_common (struct backend *backend,
			 const gchar *src, const gchar *dst,
			 const guint8 *data, guint len)
{
  gint res;
  GByteArray *rx_msg;
  GByteArray *tx_msg = elektron_new_msg (data, len);
  gchar *dst_cp1252 = elektron_get_cp1252 (dst);
  gchar *src_cp1252 = elektron_get_cp1252 (src);

  g_byte_array_append (tx_msg, (guchar *) src_cp1252,
		       strlen (src_cp1252) + 1);
  g_byte_array_append (tx_msg, (guchar *) dst_cp1252,
		       strlen (dst_cp1252) + 1);

  g_free (src_cp1252);
  g_free (dst_cp1252);

  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }
  //Response: x, x, x, x, 0xa1, [0 (error), 1 (success)]...
  if (elektron_get_msg_status (rx_msg))
    {
      res = 0;
    }
  else
    {
      res = -EPERM;
      error_print ("%s (%s)", backend_strerror (backend, res),
		   elektron_get_msg_string (rx_msg));
    }
  free_msg (rx_msg);

  return res;
}

static gint
elektron_rename_sample_file (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_src_dst_common (backend, src, dst,
				  FS_SAMPLE_RENAME_FILE_REQUEST,
				  sizeof (FS_SAMPLE_RENAME_FILE_REQUEST));
}

static gint
elektron_rename_raw_file (struct backend *backend, const gchar *src,
			  const gchar *dst)
{
  gint err;
  gchar *src_with_ext = elektron_mc_add_snd_ext (src);
  gchar *dst_with_ext = elektron_mc_add_snd_ext (dst);
  err = elektron_src_dst_common (backend, src_with_ext, dst_with_ext,
				 FS_RAW_RENAME_FILE_REQUEST,
				 sizeof (FS_RAW_RENAME_FILE_REQUEST));
  g_free (src_with_ext);
  g_free (dst_with_ext);
  return err;
}

static gint
elektron_move_common_item (struct backend *backend, const gchar *src,
			   const gchar *dst, fs_init_iter_func init_iter,
			   elektron_src_dst_func mv, fs_path_func mkdir,
			   elektron_path_func rmdir)
{
  enum item_type type;
  gint res;
  gchar *src_plus;
  gchar *dst_plus;
  struct item_iterator iter;

  //Renaming is not implemented for directories so we need to implement it.

  debug_print (1, "Renaming remotely from %s to %s...", src, dst);

  type = elektron_get_path_type (backend, src, init_iter);
  if (type == ITEM_TYPE_FILE)
    {
      return mv (backend, src, dst);
    }
  else if (type == ITEM_TYPE_DIR)
    {
      res = mkdir (backend, dst);
      if (res)
	{
	  return res;
	}
      if (!init_iter (backend, &iter, src, NULL))
	{
	  while (!item_iterator_next (&iter) && !res)
	    {
	      src_plus = path_chain (PATH_INTERNAL, src, iter.item.name);
	      dst_plus = path_chain (PATH_INTERNAL, dst, iter.item.name);
	      res = elektron_move_common_item (backend, src_plus, dst_plus,
					       init_iter, mv, mkdir, rmdir);
	      g_free (src_plus);
	      g_free (dst_plus);
	    }
	  item_iterator_free (&iter);
	}
      if (!res)
	{
	  res = rmdir (backend, src);
	}
      return res;
    }
  else
    {
      return -EBADF;
    }
}

static gint
elektron_path_common (struct backend *backend, const gchar *path,
		      const guint8 *template, gint size)
{
  gint res;
  GByteArray *rx_msg;
  GByteArray *tx_msg;

  tx_msg = elektron_new_msg_path (template, size, path);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }
  //Response: x, x, x, x, 0xX0, [0 (error), 1 (success)]...
  if (elektron_get_msg_status (rx_msg))
    {
      res = 0;
    }
  else
    {
      res = -EPERM;
      debug_print (1, "Error: %s", elektron_get_msg_string (rx_msg));
    }
  free_msg (rx_msg);

  return res;
}

static gint
elektron_delete_sample (struct backend *backend, const gchar *path)
{
  return elektron_path_common (backend, path,
			       FS_SAMPLE_DELETE_FILE_REQUEST,
			       sizeof (FS_SAMPLE_DELETE_FILE_REQUEST));
}

static gint
elektron_delete_samples_dir (struct backend *backend, const gchar *path)
{
  return elektron_path_common (backend, path, FS_SAMPLE_DELETE_DIR_REQUEST,
			       sizeof (FS_SAMPLE_DELETE_DIR_REQUEST));
}

static gboolean
elektron_sample_file_exists (struct backend *backend, const gchar *path)
{
  gint res = elektron_path_common (backend, path,
				   FS_SAMPLE_GET_FILE_INFO_FROM_PATH_REQUEST,
				   sizeof
				   (FS_SAMPLE_GET_FILE_INFO_FROM_PATH_REQUEST));
  return res == 0;
}

static gboolean
elektron_raw_file_exists (struct backend *backend, const gchar *path)
{
  gchar *name_with_ext = elektron_mc_add_snd_ext (path);
  gint res = elektron_path_common (backend, path,
				   FS_RAW_GET_FILE_INFO_FROM_PATH_REQUEST,
				   sizeof
				   (FS_RAW_GET_FILE_INFO_FROM_PATH_REQUEST));
  g_free (name_with_ext);
  return res == 0;
}

static gint
elektron_delete_raw (struct backend *backend, const gchar *path)
{
  gint ret;
  gchar *path_with_ext = elektron_mc_add_snd_ext (path);
  ret = elektron_path_common (backend, path_with_ext,
			      FS_RAW_DELETE_FILE_REQUEST,
			      sizeof (FS_RAW_DELETE_FILE_REQUEST));
  g_free (path_with_ext);
  return ret;
}

static gint
elektron_delete_raw_dir (struct backend *backend, const gchar *path)
{
  return elektron_path_common (backend, path, FS_RAW_DELETE_DIR_REQUEST,
			       sizeof (FS_RAW_DELETE_DIR_REQUEST));
}

static gint
elektron_create_samples_dir (struct backend *backend, const gchar *path)
{
  return elektron_path_common (backend, path, FS_SAMPLE_CREATE_DIR_REQUEST,
			       sizeof (FS_SAMPLE_CREATE_DIR_REQUEST));
}

static gint
elektron_move_samples_item (struct backend *backend, const gchar *src,
			    const gchar *dst)
{
  return elektron_move_common_item (backend, src, dst,
				    elektron_read_samples_dir,
				    elektron_rename_sample_file,
				    elektron_create_samples_dir,
				    elektron_delete_samples_dir);
}

static gint
elektron_create_raw_dir (struct backend *backend, const gchar *path)
{
  return elektron_path_common (backend, path, FS_RAW_CREATE_DIR_REQUEST,
			       sizeof (FS_RAW_CREATE_DIR_REQUEST));
}

static gint
elektron_move_raw_item (struct backend *backend, const gchar *src,
			const gchar *dst)
{
  return elektron_move_common_item (backend, src, dst,
				    elektron_read_raw_dir,
				    elektron_rename_raw_file,
				    elektron_create_raw_dir,
				    elektron_delete_raw_dir);
}

static gint
elektron_delete_common_item (struct backend *backend, const gchar *path,
			     fs_init_iter_func init_iter,
			     elektron_path_func rmdir, elektron_path_func rm)
{
  enum item_type type;
  gchar *new_path;
  struct item_iterator iter;
  gint res;

  type = elektron_get_path_type (backend, path, init_iter);
  if (type == ITEM_TYPE_FILE)
    {
      return rm (backend, path);
    }
  else if (type == ITEM_TYPE_DIR)
    {
      debug_print (1, "Deleting %s samples dir...", path);

      if (init_iter (backend, &iter, path, NULL))
	{
	  error_print ("Error while opening samples dir %s dir", path);
	  res = -EINVAL;
	}
      else
	{
	  res = 0;
	  while (!res && !item_iterator_next (&iter))
	    {
	      new_path = path_chain (PATH_INTERNAL, path, iter.item.name);
	      res = res || elektron_delete_common_item (backend, new_path,
							init_iter, rmdir, rm);
	      g_free (new_path);
	    }
	  item_iterator_free (&iter);
	}
      return res || rmdir (backend, path);
    }
  else
    {
      return -EBADF;
    }
}

static gint
elektron_delete_samples_item (struct backend *backend, const gchar *path)
{
  return elektron_delete_common_item (backend, path,
				      elektron_read_samples_dir,
				      elektron_delete_samples_dir,
				      elektron_delete_sample);
}

static gint
elektron_delete_raw_item (struct backend *backend, const gchar *path)
{
  return elektron_delete_common_item (backend, path,
				      elektron_read_raw_dir,
				      elektron_delete_raw_dir,
				      elektron_delete_raw);
}

static gint
elektron_upload_smplrw (struct backend *backend, const gchar *path,
			struct idata *smplrw, struct task_control *control,
			elektron_msg_path_len_func new_msg_open_write,
			elektron_msg_write_blk_func new_msg_write_blk,
			elektron_msg_id_len_func new_msg_close_write)
{
  GByteArray *tx_msg;
  GByteArray *rx_msg;
  guint transferred;
  guint32 id;
  int i;
  gboolean active;
  gint res = 0;
  GByteArray *input = smplrw->content;

  //If the file already exists the device makes no difference between creating a new file and creating an already existent file.
  //Also, the new file would be discarded if an upload is not completed.

  tx_msg = new_msg_open_write (path, input->len);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
  if (!rx_msg)
    {
      return -EIO;
    }

  //Response: x, x, x, x, 0xc0, [0 (error), 1 (success)], id, frames
  res = elektron_get_smplrw_info_from_msg (rx_msg, &id, NULL);
  if (res)
    {
      error_print ("%s (%s)", backend_strerror (backend, res),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      return res;
    }
  free_msg (rx_msg);

  transferred = 0;
  i = 0;

  active = controllable_is_active (&control->controllable);

  while (transferred < input->len && active)
    {
      tx_msg = new_msg_write_blk (id, input, &transferred, i, smplrw->info);
      rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
      if (!rx_msg)
	{
	  return -EIO;
	}
      //Response: x, x, x, x, 0xc2, [0 (error), 1 (success)]...
      if (!elektron_get_msg_status (rx_msg))
	{
	  error_print ("Unexpected status");
	}
      free_msg (rx_msg);
      i++;

      task_control_set_progress (control, transferred / (double) input->len);

      active = controllable_is_active (&control->controllable);

      g_usleep (BE_REST_TIME_US);
    }

  debug_print (2, "%d bytes sent", transferred);

  if (active)
    {
      tx_msg = new_msg_close_write (id, transferred);
      rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
      if (!rx_msg)
	{
	  return -EIO;
	}
      //Response: x, x, x, x, 0xc1, [0 (error), 1 (success)]...
      if (!elektron_get_msg_status (rx_msg))
	{
	  error_print ("Unexpected status");
	}
      free_msg (rx_msg);
    }

  return res;
}

gint
elektron_upload_sample_part (struct backend *backend, const gchar *path,
			     struct idata *sample,
			     struct task_control *control)
{
  return elektron_upload_smplrw (backend, path, sample, control,
				 elektron_new_msg_open_sample_write,
				 elektron_new_msg_write_sample_blk,
				 elektron_new_msg_close_sample_write);
}

static gint
elektron_upload_sample (struct backend *backend, const gchar *path,
			struct idata *sample, struct task_control *control)
{
  control->parts = 1;
  control->part = 0;
  return elektron_upload_sample_part (backend, path, sample, control);
}

static gint
elektron_upload_raw (struct backend *backend, const gchar *path,
		     struct idata *raw, struct task_control *control)
{
  return elektron_upload_smplrw (backend, path, raw, control,
				 elektron_new_msg_open_raw_write,
				 elektron_new_msg_write_raw_blk,
				 elektron_new_msg_close_raw_write);
}

static GByteArray *
elektron_new_msg_open_sample_read (const gchar *path)
{
  return elektron_new_msg_path (FS_SAMPLE_OPEN_FILE_READER_REQUEST,
				sizeof
				(FS_SAMPLE_OPEN_FILE_READER_REQUEST), path);
}

static GByteArray *
elektron_new_msg_open_raw_read (const gchar *path)
{
  return elektron_new_msg_path (FS_RAW_OPEN_FILE_READER_REQUEST,
				sizeof
				(FS_RAW_OPEN_FILE_READER_REQUEST), path);
}

static void
elektron_copy_sample_data (GByteArray *input, GByteArray *output)
{
  gint i;
  gint16 v;
  gint16 *frame = (gint16 *) input->data;

  for (i = 0; i < input->len; i += sizeof (gint16))
    {
      v = g_ntohs (*frame);
      g_byte_array_append (output, (guint8 *) & v, sizeof (gint16));
      frame++;
    }
}

static void
elektron_copy_raw_data (GByteArray *input, GByteArray *output)
{
  g_byte_array_append (output, input->data, input->len);
}

static gint
elektron_download_smplrw (struct backend *backend, const gchar *path,
			  struct idata *smplrw, struct task_control *control,
			  elektron_msg_path_func new_msg_open_read,
			  guint read_offset,
			  elektron_msg_read_blk_func new_msg_read_blk,
			  elektron_msg_id_func new_msg_close_read,
			  elektron_copy_array copy_array)
{
  struct sample_info *sample_info = NULL;
  struct elektron_sample_header *elektron_sample_header;
  GByteArray *tx_msg, *rx_msg;
  GByteArray *array, *output;
  guint32 id;
  guint frames;
  guint next_block_start;
  guint req_size;
  guint offset;
  gboolean active;
  gint res;

  tx_msg = new_msg_open_read (path);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
  if (!rx_msg)
    {
      return -EIO;
    }
  res = elektron_get_smplrw_info_from_msg (rx_msg, &id, &frames);
  if (res)
    {
      error_print ("Error while openning '%s': %s (%s)", path,
		   backend_strerror (backend, res),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      return res;
    }
  free_msg (rx_msg);

  debug_print (2, "%d frames to download", frames);

  active = controllable_is_active (&control->controllable);

  output = g_byte_array_new ();
  array = g_byte_array_new ();
  res = 0;
  next_block_start = 0;
  offset = read_offset;
  while (next_block_start < frames && active)
    {
      req_size =
	frames - next_block_start >
	DATA_TRANSF_BLOCK_BYTES ? DATA_TRANSF_BLOCK_BYTES : frames -
	next_block_start;
      tx_msg = new_msg_read_blk (id, next_block_start, req_size);
      rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
      if (!rx_msg)
	{
	  res = -EIO;
	  goto cleanup;
	}
      g_byte_array_append (array, &rx_msg->data[FS_SAMPLES_PAD_RES + offset],
			   req_size - offset);

      next_block_start += req_size;
      //Only in the first iteration. It has no effect for the raw filesystem (M:C) as offset is 0.
      if (offset)
	{
	  offset = 0;
	  elektron_sample_header =
	    (struct elektron_sample_header *)
	    &rx_msg->data[FS_SAMPLES_PAD_RES];
	  sample_info = sample_info_new (FALSE);
	  sample_info->frames = frames;
	  sample_info->loop_start =
	    g_ntohl (elektron_sample_header->loop_start);
	  sample_info->loop_end = g_ntohl (elektron_sample_header->loop_end);
	  sample_info->loop_type = elektron_sample_header->loop_type;
	  sample_info->rate = g_ntohl (elektron_sample_header->rate);	//In the case of the RAW filesystem is not used and it is harmless.
	  sample_info->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
	  sample_info->channels = elektron_sample_header->stereo + 1;
	  debug_print (2, "Loop start at %d, loop end at %d",
		       sample_info->loop_start, sample_info->loop_end);
	}

      free_msg (rx_msg);

      task_control_set_progress (control, next_block_start / (double) frames);

      active = controllable_is_active (&control->controllable);

      g_usleep (BE_REST_TIME_US);
    }

  debug_print (2, "%d bytes received", next_block_start);

  if (active)
    {
      copy_array (array, output);
    }
  else
    {
      res = -1;
    }

  tx_msg = new_msg_close_read (id);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
  if (!rx_msg)
    {
      res = -EIO;
      goto cleanup;
    }
  //Response: x, x, x, x, 0xb1, 00 00 00 0a 00 01 65 de (sample id and received bytes)
  free_msg (rx_msg);

cleanup:
  free_msg (array);
  if (res)
    {
      g_byte_array_free (output, TRUE);
      g_free (sample_info);
    }
  else
    {
      idata_init (smplrw, output, g_path_get_basename (path), sample_info,
		  sample_info ? sample_info_free : NULL);
    }
  return res;
}

gint
elektron_download_sample_part (struct backend *backend, const gchar *path,
			       struct idata *sample,
			       struct task_control *control)
{
  return elektron_download_smplrw (backend, path, sample, control,
				   elektron_new_msg_open_sample_read,
				   sizeof (struct elektron_sample_header),
				   elektron_new_msg_read_sample_blk,
				   elektron_new_msg_close_sample_read,
				   elektron_copy_sample_data);
}

static gint
elektron_download_sample (struct backend *backend, const gchar *path,
			  struct idata *file, struct task_control *control)
{
  control->parts = 1;
  control->part = 0;
  return elektron_download_sample_part (backend, path, file, control);
}

static gint
elektron_download_raw (struct backend *backend, const gchar *path,
		       struct idata *file, struct task_control *control)
{
  gint ret;
  gchar *path_with_ext = elektron_mc_add_snd_ext (path);
  ret = elektron_download_smplrw (backend, path_with_ext, file, control,
				  elektron_new_msg_open_raw_read,
				  0, elektron_new_msg_read_raw_blk,
				  elektron_new_msg_close_raw_read,
				  elektron_copy_raw_data);
  g_free (path_with_ext);
  return ret;
}

static GByteArray *
elektron_new_msg_upgrade_os_start (guint size)
{
  GByteArray *msg = elektron_new_msg (OS_UPGRADE_START_REQUEST,
				      sizeof (OS_UPGRADE_START_REQUEST));

  memcpy (&msg->data[5], &size, sizeof (guint32));

  return msg;
}

static guint32
elektron_crc (guint8 *data, guint32 len)
{
  return crc32 (0xffffffff, data, len);
}

static GByteArray *
elektron_new_msg_upgrade_os_write (GByteArray *os_data, gint *offset)
{
  GByteArray *msg = elektron_new_msg (OS_UPGRADE_WRITE_RESPONSE,
				      sizeof (OS_UPGRADE_WRITE_RESPONSE));
  guint len;
  guint32 crc;
  guint32 aux32;

  if (*offset + OS_TRANSF_BLOCK_BYTES < os_data->len)
    {
      len = OS_TRANSF_BLOCK_BYTES;
    }
  else
    {
      len = os_data->len - *offset;
    }

  crc = elektron_crc (&os_data->data[*offset], len);

  debug_print (2, "CRC: %0x", crc);

  aux32 = g_htonl (crc);
  memcpy (&msg->data[5], &aux32, sizeof (guint32));
  aux32 = g_htonl (len);
  memcpy (&msg->data[9], &aux32, sizeof (guint32));
  aux32 = g_htonl (*offset);
  memcpy (&msg->data[13], &aux32, sizeof (guint32));

  g_byte_array_append (msg, &os_data->data[*offset], len);

  *offset = *offset + len;

  return msg;
}

static gint
elektron_upgrade_os (struct backend *backend, struct sysex_transfer *transfer,
		     struct controllable *controllable)
{
  GByteArray *tx_msg;
  GByteArray *rx_msg;
  gint8 op;
  gint offset;
  gint res = 0;

  tx_msg = elektron_new_msg_upgrade_os_start (transfer->raw->len);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, controllable);

  if (!rx_msg)
    {
      res = -EIO;
      goto end;
    }
  //Response: x, x, x, x, 0xd0, [0 (ok), 1 (error)]...
  op = elektron_get_msg_status (rx_msg);
  if (op)
    {
      res = -EIO;
      error_print ("%s (%s)", backend_strerror (backend, res),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      goto end;
    }

  free_msg (rx_msg);

  offset = 0;
  while (offset < transfer->raw->len)
    {
      tx_msg = elektron_new_msg_upgrade_os_write (transfer->raw, &offset);
      rx_msg = elektron_tx_and_rx (backend, tx_msg, controllable);

      if (!rx_msg)
	{
	  res = -EIO;
	  break;
	}
      //Response: x, x, x, x, 0xd1, int32, [0..3]...
      op = rx_msg->data[9];
      if (op == 1)
	{
	  break;
	}
      else if (op > 1)
	{
	  res = -EIO;
	  error_print ("%s (%s)", backend_strerror (backend, res),
		       elektron_get_msg_string (rx_msg));
	  free_msg (rx_msg);
	  break;
	}

      free_msg (rx_msg);

      g_usleep (BE_REST_TIME_US);

      if (!controllable_is_active (controllable))
	{
	  res = -ECANCELED;
	  goto end;
	}
    }

end:
  return res;
}

static gint
elektron_get_storage_stats (struct backend *backend, guint8 type,
			    struct backend_storage_stats *statfs,
			    const gchar *path)
{
  GByteArray *tx_msg, *rx_msg;
  gint8 op;
  guint64 *v;
  gint res = 0;
  guint8 fsid;
  struct elektron_data *data = backend->data;

  if (!(type & data->dev_desc.storage))
    {
      return -EINVAL;
    }

  fsid = log2l (type);

  tx_msg = elektron_new_msg_uint8 (STORAGEINFO_REQUEST,
				   sizeof (STORAGEINFO_REQUEST), fsid + 1);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  op = elektron_get_msg_status (rx_msg);
  if (!op)
    {
      error_print ("%s (%s)", backend_strerror (backend, -EIO),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      return -EIO;
    }

  snprintf (statfs->name, LABEL_MAX, "%s", FS_TYPE_NAMES[fsid]);
  v = (guint64 *) & rx_msg->data[6];
  statfs->bfree = GUINT64_FROM_BE (*v);
  v = (guint64 *) & rx_msg->data[14];
  statfs->bsize = GUINT64_FROM_BE (*v);

  free_msg (rx_msg);

  return res ? res : type << 1 < data->dev_desc.storage;
}

static gint
elektron_next_data_entry (struct item_iterator *iter)
{
  gchar *name_cp1252;
  guint32 *data32;
  guint16 *data16;
  guint8 type;
  guint8 has_children;
  guint32 id;
  struct elektron_iterator_data *data = iter->data;

  if (data->pos == data->msg->len)
    {
      //A data directory only contains either files or directories.
      //If the last visited item was a directory, there are no more slots to visit.
      if (iter->item.type == ITEM_TYPE_DIR
	  || iter->item.id >= data->max_slots)
	{
	  return -ENOENT;
	}

      goto not_found;
    }

  name_cp1252 = (gchar *) & data->msg->data[data->pos];

  if (data->max_slots != -1 && iter->item.type != ITEM_TYPE_DIR)
    {
      guint32 pos = data->pos + strlen (name_cp1252) + 3;
      data32 = (guint32 *) & data->msg->data[pos];
      id = g_ntohl (*data32);
      if (id > iter->item.id + 1)
	{
	  goto not_found;
	}
    }

  elektron_item_set_name (&iter->item, name_cp1252);
  data->pos += strlen (name_cp1252) + 1;
  has_children = data->msg->data[data->pos];
  data->pos++;
  type = data->msg->data[data->pos];
  data->pos++;

  switch (type)
    {
    case 1:
      iter->item.type = ITEM_TYPE_DIR;
      data->pos += sizeof (guint32);	// child entries
      iter->item.size = 0;
      iter->item.id = -1;
      data->operations = 0;
      data->has_valid_data = 0;
      data->has_metadata = 0;
      iter->item.object_info[0] = 0;
      break;
    case 2:
      iter->item.type = has_children ? ITEM_TYPE_DIR : ITEM_TYPE_FILE;

      data32 = (guint32 *) & data->msg->data[data->pos];
      iter->item.id = has_children ? -1 : g_ntohl (*data32);
      data->pos += sizeof (gint32);

      data32 = (guint32 *) & data->msg->data[data->pos];
      iter->item.size = g_ntohl (*data32);
      data->pos += sizeof (guint32);

      data16 = (guint16 *) & data->msg->data[data->pos];
      data->operations = g_ntohs (*data16);
      data->pos += sizeof (guint16);

      data->has_valid_data = data->msg->data[data->pos];
      data->pos++;

      data->has_metadata = data->msg->data[data->pos];
      data->pos++;

      iter->item.object_info[0] = 0;
      if (data->load_metadata && data->has_metadata &&
	  data->mode == ITER_MODE_DATA_SND &&
	  preferences_get_boolean (PREF_KEY_ELEKTRON_LOAD_SOUND_TAGS))
	{
	  gchar metadata_path[PATH_MAX];
	  struct idata output;
	  struct task_control control;

	  controllable_init (&control.controllable);
	  control.callback = NULL;

	  snprintf (metadata_path, PATH_MAX, "%s/%d/%s", iter->dir,
		    iter->item.id, FS_DATA_METADATA_FILE);
	  debug_print (2, "Reading metadata from %s...", metadata_path);
	  if (!elektron_download_data_snd (data->backend, metadata_path,
					   &output, &control))
	    {
	      gchar *s;
	      gboolean first = TRUE;
	      GString *info = g_string_new (NULL);
	      GSList *tags =
		elektron_pkg_get_tags_from_snd_metadata (output.content);
	      GSList *e = tags;

	      while (e)
		{
		  gchar *tag = (gchar *) e->data;
		  const gchar *separator =
		    first ? "" : ELEKTROID_TOKEN_SEPARATOR;
		  g_string_append_printf (info, "%s%s", separator, tag);
		  first = FALSE;
		  e = e->next;
		}

	      s = g_string_free (info, FALSE);
	      item_set_object_info (&iter->item, "tags=%s", s);
	      g_free (s);
	      g_slist_free_full (tags, g_free);

	      idata_clear (&output);
	    }

	  controllable_clear (&control.controllable);
	}

      elektron_set_slot (&iter->item);

      break;
    default:
      error_print ("Unrecognized data entry: %d", iter->item.type);
      break;
    }

  return 0;

not_found:
  iter->item.type = ITEM_TYPE_FILE;
  iter->item.name[0] = 0;
  iter->item.size = 0;
  iter->item.id++;
  elektron_set_slot (&iter->item);
  data->operations = 0;
  data->has_valid_data = 0;
  data->has_metadata = 0;
  iter->item.object_info[0] = 0;
  return 0;
}

static gchar *
elektron_add_prefix_to_path (const gchar *dir, const gchar *prefix)
{
  gchar *full;

  if (prefix)
    {
      GString *str = g_string_new (NULL);
      g_string_append_printf (str, "%s%s", prefix, dir);
      full = g_string_free (str, FALSE);
    }
  else
    {
      full = strdup (dir);
    }

  return full;
}

static gint
elektron_read_data_dir_prefix (struct backend *backend,
			       struct item_iterator *iter,
			       const gchar *dir, const char *prefix,
			       enum elektron_iterator_mode mode,
			       guint32 start_pos, gint32 max_slots)
{
  int res;
  GByteArray *tx_msg;
  GByteArray *rx_msg;
  gchar *dir_w_prefix = elektron_add_prefix_to_path (dir, prefix);

  tx_msg = elektron_new_msg_list (dir_w_prefix, 0, 0, 1);
  g_free (dir_w_prefix);
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  res = elektron_get_msg_status (rx_msg);
  if (!res)
    {
      free_msg (rx_msg);
      return -ENOTDIR;
    }

  return elektron_init_iterator (backend, iter, dir, rx_msg,
				 elektron_next_data_entry, mode, start_pos,
				 max_slots);
}

static gint
elektron_read_data_dir_any (struct backend *backend,
			    struct item_iterator *iter, const gchar *dir,
			    const gchar **extensions)
{
  return elektron_read_data_dir_prefix (backend, iter, dir, NULL,
					ITER_MODE_DATA, FS_DATA_START_POS,
					-1);
}

static gint
elektron_read_data_dir_prj (struct backend *backend,
			    struct item_iterator *iter, const gchar *dir,
			    const gchar **extensions)
{
  return elektron_read_data_dir_prefix (backend, iter, dir,
					FS_DATA_PRJ_PREFIX, ITER_MODE_DATA,
					FS_DATA_START_POS, PROJECT_SLOTS);
}

static gint
elektron_read_data_dir_snd (struct backend *backend,
			    struct item_iterator *iter, const gchar *dir,
			    const gchar **extensions)
{
  return elektron_read_data_dir_prefix (backend, iter, dir,
					FS_DATA_SND_PREFIX,
					ITER_MODE_DATA_SND, FS_DATA_START_POS,
					SOUND_SLOTS);
}

static gint
elektron_read_data_dir_pst (struct backend *backend,
			    struct item_iterator *iter, const gchar *dir,
			    const gchar **extensions)
{
  struct elektron_data *data = backend->data;
  gint32 slots = data->dev_desc.id == ELEKTRON_AH_FX_ID ? AH_FX_SLOTS : AH_SLOTS;	//Analog Heat +FX has 512 presets
  return elektron_read_data_dir_prefix (backend, iter, dir,
					FS_DATA_PST_PREFIX,
					ITER_MODE_DATA, FS_DATA_START_POS,
					slots);
}

static gint
elektron_read_data_dir_sample (struct backend *backend,
			       struct item_iterator *iter, const gchar *dir,
			       const gchar **extensions)
{
  return elektron_read_data_dir_prefix (backend, iter, dir,
					FS_DATA_SAMPLES_PREFIX,
					ITER_MODE_DATA, FS_DATA_START_POS,
					SAMPLE_SLOTS);
}

static gint
elektron_dst_src_data_prefix_common (struct backend *backend,
				     const gchar *src, const gchar *dst,
				     const char *prefix,
				     const guint8 *op_data, guint len)
{
  gint res;
  char *src_w_prefix = elektron_add_prefix_to_path (src, prefix);
  char *dst_w_prefix = elektron_add_prefix_to_path (dst, prefix);

  res = elektron_src_dst_common (backend, src_w_prefix, dst_w_prefix,
				 op_data, len);
  g_free (src_w_prefix);
  g_free (dst_w_prefix);

  return res;
}

static gint
elektron_move_data_item_prefix (struct backend *backend, const gchar *src,
				const gchar *dst, const char *prefix)
{
  return elektron_dst_src_data_prefix_common (backend, src, dst, prefix,
					      DATA_MOVE_REQUEST,
					      sizeof (DATA_MOVE_REQUEST));
}

static gint
elektron_move_data_item_any (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_move_data_item_prefix (backend, src, dst, NULL);
}

static gint
elektron_move_data_item_prj (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_move_data_item_prefix (backend, src, dst,
					 FS_DATA_PRJ_PREFIX);
}

static gint
elektron_move_data_item_snd (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_move_data_item_prefix (backend, src, dst,
					 FS_DATA_SND_PREFIX);
}

static gint
elektron_move_data_item_pst (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_move_data_item_prefix (backend, src, dst,
					 FS_DATA_PST_PREFIX);
}

static gint
elektron_move_data_item_sample (struct backend *backend, const gchar *src,
				const gchar *dst)
{
  return elektron_move_data_item_prefix (backend, src, dst,
					 FS_DATA_SAMPLES_PREFIX);
}

static gint
elektron_copy_data_item_prefix (struct backend *backend, const gchar *src,
				const gchar *dst, const gchar *prefix)
{
  return elektron_dst_src_data_prefix_common (backend, src, dst, prefix,
					      DATA_COPY_REQUEST,
					      sizeof (DATA_COPY_REQUEST));
}

static gint
elektron_copy_data_item_any (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_copy_data_item_prefix (backend, src, dst, NULL);
}

static gint
elektron_copy_data_item_prj (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_copy_data_item_prefix (backend, src, dst,
					 FS_DATA_PRJ_PREFIX);
}

static gint
elektron_copy_data_item_snd (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_copy_data_item_prefix (backend, src, dst,
					 FS_DATA_SND_PREFIX);
}

static gint
elektron_copy_data_item_pst (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_copy_data_item_prefix (backend, src, dst,
					 FS_DATA_PST_PREFIX);
}

static gint
elektron_copy_data_item_sample (struct backend *backend, const gchar *src,
				const gchar *dst)
{
  return elektron_copy_data_item_prefix (backend, src, dst,
					 FS_DATA_SAMPLES_PREFIX);
}

static gint
elektron_path_data_prefix_common (struct backend *backend,
				  const gchar *path, const char *prefix,
				  const guint8 *op_data, guint len)
{
  gint res;
  char *path_w_prefix = elektron_add_prefix_to_path (path, prefix);

  res = elektron_path_common (backend, path_w_prefix, op_data, len);
  g_free (path_w_prefix);

  return res;
}

static gint
elektron_clear_data_item_prefix (struct backend *backend,
				 const gchar *path, const gchar *prefix)
{
  return elektron_path_data_prefix_common (backend, path, prefix,
					   DATA_CLEAR_REQUEST,
					   sizeof (DATA_CLEAR_REQUEST));
}

static gint
elektron_clear_data_item_any (struct backend *backend, const gchar *path)
{
  return elektron_clear_data_item_prefix (backend, path, NULL);
}

static gint
elektron_clear_data_item_prj (struct backend *backend, const gchar *path)
{
  return elektron_clear_data_item_prefix (backend, path, FS_DATA_PRJ_PREFIX);
}

static gint
elektron_clear_data_item_snd (struct backend *backend, const gchar *path)
{
  return elektron_clear_data_item_prefix (backend, path, FS_DATA_SND_PREFIX);
}

static gint
elektron_clear_data_item_pst (struct backend *backend, const gchar *path)
{
  return elektron_clear_data_item_prefix (backend, path, FS_DATA_PST_PREFIX);
}

static gint
elektron_clear_data_item_sample (struct backend *backend, const gchar *path)
{
  return elektron_clear_data_item_prefix (backend, path,
					  FS_DATA_SAMPLES_PREFIX);
}

static gint
elektron_swap_data_item_prefix (struct backend *backend, const gchar *src,
				const gchar *dst, const gchar *prefix)
{
  return elektron_dst_src_data_prefix_common (backend, src, dst, prefix,
					      DATA_SWAP_REQUEST,
					      sizeof (DATA_SWAP_REQUEST));
}

static gint
elektron_swap_data_item_any (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_swap_data_item_prefix (backend, src, dst, NULL);
}

static gint
elektron_swap_data_item_prj (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_swap_data_item_prefix (backend, src, dst,
					 FS_DATA_PRJ_PREFIX);
}

static gint
elektron_swap_data_item_snd (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_swap_data_item_prefix (backend, src, dst,
					 FS_DATA_SND_PREFIX);
}

static gint
elektron_swap_data_item_pst (struct backend *backend, const gchar *src,
			     const gchar *dst)
{
  return elektron_swap_data_item_prefix (backend, src, dst,
					 FS_DATA_PST_PREFIX);
}

static gint
elektron_swap_data_item_sample (struct backend *backend, const gchar *src,
				const gchar *dst)
{
  return elektron_swap_data_item_prefix (backend, src, dst,
					 FS_DATA_SAMPLES_PREFIX);
}

static gint
elektron_open_datum (struct backend *backend, const gchar *path,
		     guint32 *jid, gint mode, guint32 size)
{
  guint32 *data32;
  guint32 sizebe;
  guint32 chunk_size;
  guint8 compression;
  GByteArray *rx_msg;
  GByteArray *tx_msg;
  const guint8 *data;
  guint len;
  gchar *path_cp1252;
  gint res = 0;

  if (mode == O_RDONLY)
    {
      data = DATA_READ_OPEN_REQUEST;
      len = sizeof (DATA_READ_OPEN_REQUEST);
    }
  else if (mode == O_WRONLY)
    {
      data = DATA_WRITE_OPEN_REQUEST;
      len = sizeof (DATA_WRITE_OPEN_REQUEST);
    }
  else
    {
      return -EINVAL;
    }

  tx_msg = elektron_new_msg (data, len);

  path_cp1252 = elektron_get_cp1252 (path);

  // Samples are the only payload type we want to actually work with.
  compression = g_str_has_prefix (path, FS_DATA_SAMPLES_PREFIX) ? 0 : 1;

  if (mode == O_RDONLY)
    {
      g_byte_array_append (tx_msg, (guint8 *) path_cp1252,
			   strlen (path_cp1252) + 1);
      chunk_size = g_htonl (DATA_TRANSF_BLOCK_BYTES);
      g_byte_array_append (tx_msg, (guint8 *) & chunk_size, sizeof (guint32));
      g_byte_array_append (tx_msg, &compression, sizeof (guint8));
    }

  if (mode == O_WRONLY)
    {
      sizebe = g_htonl (size);
      g_byte_array_append (tx_msg, (guint8 *) & sizebe, sizeof (guint32));
      g_byte_array_append (tx_msg, (guint8 *) path_cp1252,
			   strlen (path_cp1252) + 1);
    }

  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      res = -EIO;
      goto cleanup;
    }

  if (!elektron_get_msg_status (rx_msg))
    {
      res = -EPERM;
      error_print ("%s (%s)", backend_strerror (backend, res),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      goto cleanup;
    }

  data32 = (guint32 *) & rx_msg->data[6];
  *jid = g_ntohl (*data32);

  if (mode == O_RDONLY)
    {
      data32 = (guint32 *) & rx_msg->data[10];
      chunk_size = g_ntohl (*data32);

      compression = rx_msg->data[14];

      debug_print (1,
		   "Open datum info: job id: %d; chunk size: %d; compression: %d",
		   *jid, chunk_size, compression);
    }

  if (mode == O_WRONLY)
    {
      debug_print (1, "Open datum info: job id: %d", *jid);
    }

  free_msg (rx_msg);

cleanup:
  g_free (path_cp1252);
  return res;
}

static gint
elektron_close_datum (struct backend *backend,
		      guint32 jid, gint mode, guint32 wsize)
{
  guint32 jidbe;
  guint32 wsizebe;
  guint32 r_jid;
  guint32 asize;
  guint32 *data32;
  GByteArray *rx_msg;
  GByteArray *tx_msg;
  const guint8 *data;
  guint len;

  if (mode == O_RDONLY)
    {
      data = DATA_READ_CLOSE_REQUEST;
      len = sizeof (DATA_READ_CLOSE_REQUEST);
    }
  else if (mode == O_WRONLY)
    {
      data = DATA_WRITE_CLOSE_REQUEST;
      len = sizeof (DATA_WRITE_CLOSE_REQUEST);
    }
  else
    {
      return -EINVAL;
    }

  tx_msg = elektron_new_msg (data, len);

  jidbe = g_htonl (jid);
  g_byte_array_append (tx_msg, (guchar *) & jidbe, sizeof (guint32));

  if (mode == O_WRONLY)
    {
      wsizebe = g_htonl (wsize);
      g_byte_array_append (tx_msg, (guchar *) & wsizebe, sizeof (guint32));
    }

  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  if (!elektron_get_msg_status (rx_msg))
    {
      error_print ("%s (%s)", backend_strerror (backend, -EPERM),
		   elektron_get_msg_string (rx_msg));
      free_msg (rx_msg);
      return -EPERM;
    }

  data32 = (guint32 *) & rx_msg->data[6];
  r_jid = g_ntohl (*data32);

  data32 = (guint32 *) & rx_msg->data[10];
  asize = g_ntohl (*data32);

  debug_print (1, "Close datum info: job id: %d; size: %d", r_jid, asize);

  free_msg (rx_msg);

  if (mode == O_WRONLY && asize != wsize)
    {
      error_print
	("Actual download bytes (%d) differs from expected ones (%d)",
	 asize, wsize);
      return -EINVAL;
    }

  return 0;
}

static gint
elektron_download_data_prefix (struct backend *backend, const gchar *path,
			       struct idata *data,
			       struct task_control *control,
			       const gchar *prefix)
{
  gint err;
  guint32 seq;
  guint32 seqbe;
  guint32 jid;
  guint32 r_jid;
  guint32 r_seq;
  guint32 status;
  guint8 last;
  guint32 hash;
  guint32 *data32;
  guint32 jidbe;
  guint32 data_size;
  gboolean active;
  GByteArray *rx_msg, *tx_msg, *content;
  gchar *path_w_prefix, *basename;

  basename = g_path_get_basename (path);
  err = strcmp (basename, FS_DATA_METADATA_FILE);
  g_free (basename);
  if (err)
    {
      guint id;
      err = common_slot_get_id_from_path (path, &id);
      if (err)
	{
	  return err;
	}
    }

  path_w_prefix = elektron_add_prefix_to_path (path, prefix);
  err = elektron_open_datum (backend, path_w_prefix, &jid, O_RDONLY, 0);
  g_free (path_w_prefix);
  if (err)
    {
      return -EIO;
    }

  g_usleep (BE_REST_TIME_US);

  content = g_byte_array_sized_new (4 * MI);

  active = controllable_is_active (&control->controllable);

  jidbe = g_htonl (jid);
  err = 0;
  seq = 0;
  last = 0;
  while (!last && active)
    {
      tx_msg = elektron_new_msg (DATA_READ_PARTIAL_REQUEST,
				 sizeof (DATA_READ_PARTIAL_REQUEST));
      g_byte_array_append (tx_msg, (guint8 *) & jidbe, sizeof (guint32));
      seqbe = g_htonl (seq);
      g_byte_array_append (tx_msg, (guint8 *) & seqbe, sizeof (guint32));
      rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable);
      if (!rx_msg)
	{
	  err = -EIO;
	  break;
	}

      if (!elektron_get_msg_status (rx_msg))
	{
	  err = -EPERM;
	  error_print ("%s (%s)", backend_strerror (backend, err),
		       elektron_get_msg_string (rx_msg));
	  free_msg (rx_msg);
	  break;
	}

      data32 = (guint32 *) & rx_msg->data[6];
      r_jid = g_ntohl (*data32);

      data32 = (guint32 *) & rx_msg->data[10];
      r_seq = g_ntohl (*data32);

      data32 = (guint32 *) & rx_msg->data[14];
      status = g_ntohl (*data32);

      last = rx_msg->data[18];

      data32 = (guint32 *) & rx_msg->data[19];
      hash = g_ntohl (*data32);

      data32 = (guint32 *) & rx_msg->data[23];
      data_size = g_ntohl (*data32);

      if (data_size)
	{
	  debug_print (1,
		       "Read datum info: job id: %d; last: %d; seq: %d; status: %d; hash: 0x%08x",
		       r_jid, last, r_seq, status, hash);

	  g_byte_array_append (content, (guint8 *) & rx_msg->data[27],
			       data_size);
	}
      else
	{
	  // Sometimes, the first message returns 0 data size and the rest of the parameters are not initialized.
	  debug_print (1,
		       "Read datum info: job id: %d; last: %d, hash: 0x%08x",
		       r_jid, last, hash);
	  status = 0;
	}

      free_msg (rx_msg);
      seq++;

      task_control_set_progress (control, status / 1000.0);

      active = controllable_is_active (&control->controllable);

      g_usleep (BE_REST_TIME_US);
    }

  if (active)
    {
      task_control_set_progress (control, 1.0);
      idata_init (data, content, NULL, NULL, NULL);
    }
  else
    {
      g_byte_array_free (content, TRUE);
    }

  return elektron_close_datum (backend, jid, O_RDONLY, 0);
}

static gint
elektron_download_data_any (struct backend *backend, const gchar *path,
			    struct idata *any, struct task_control *control)
{
  control->parts = 1;
  control->part = 0;
  return elektron_download_data_prefix (backend, path, any, control, NULL);
}

static gint
elektron_download_data_prj (struct backend *backend, const gchar *path,
			    struct idata *prj, struct task_control *control)
{
  return elektron_download_data_prefix (backend, path, prj, control,
					FS_DATA_PRJ_PREFIX);
}

static gint
elektron_download_data_snd (struct backend *backend, const gchar *path,
			    struct idata *snd, struct task_control *control)
{
  return elektron_download_data_prefix (backend, path, snd, control,
					FS_DATA_SND_PREFIX);
}

static gint
elektron_download_data_pst (struct backend *backend, const gchar *path,
			    struct idata *pst, struct task_control *control)
{
  return elektron_download_data_prefix (backend, path, pst, control,
					FS_DATA_PST_PREFIX);
}

// In this case, data_sample contains the footer too.

gint
elektron_set_sample_from_data_sample (struct idata *sample,
				      struct idata *data_sample)
{
  guint size = data_sample->content->len -
    sizeof (struct elektron_data_header) -
    sizeof (struct elektron_data_sample_slot_header) -
    sizeof (struct elektron_data_footer);
  guint sample_data_start = sizeof (struct elektron_data_header) +
    sizeof (struct elektron_data_sample_slot_header);
  struct elektron_data_sample_slot_header *slot_header =
    (struct elektron_data_sample_slot_header *) &data_sample->content->data
    [sizeof (struct elektron_data_header)];
  struct elektron_data_footer *footer =
    (struct elektron_data_footer *) &data_sample->content->data
    [data_sample->content->len - sizeof (struct elektron_data_footer)];

  guint crc_start = sizeof (struct elektron_data_header);
  guint crc_size = sizeof (struct elektron_data_sample_slot_header) + size;
  guint32 crc = elektron_crc (&data_sample->content->data[crc_start],
			      crc_size);
  debug_print (1,
	       "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x",
	       crc_size, crc);

  if (crc != GUINT32_FROM_BE (footer->hash))
    {
      error_print ("CRC mismatch: calculated 0x%08x, expected 0x%08x",
		   crc, GUINT32_FROM_BE (footer->hash));
      return -1;
    }

  gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN + 1];
  snprintf (safe_name, ELEKTRON_DATA_SAMPLE_MAX_LEN + 1, "%.*s",
	    ELEKTRON_DATA_SAMPLE_MAX_LEN, slot_header->name);
  GByteArray *content = g_byte_array_sized_new (size);
  content->len = size;

  const gint16 *src =
    (const gint16 *) &data_sample->content->data[sample_data_start];
  gint16 *dest = (gint16 *) content->data;
  guint frames = size / 2;
  for (guint i = 0; i < frames; i++)
    {
      dest[i] = GINT16_FROM_BE (src[i]);
    }

  struct sample_info *sample_info;
  sample_info = sample_info_new (FALSE);
  sample_info->frames = frames;
  sample_info->rate = ELEKTRON_SAMPLE_RATE;
  sample_info->channels = 1;
  sample_info->format = SF_FORMAT_PCM_16;

  idata_init (sample, content, elektron_name_to_utf8 (safe_name),
	      sample_info, sample_info_free);

  return 0;
}

// Due to a firmware limitation when working with uncompressed data,
// footer must be transferred in a different partial request.

gint
elektron_set_data_sample_from_sample (struct idata *data_sample,
				      struct idata *data_footer,
				      struct idata *sample, guint slot)
{
  struct sample_info *sample_info;
  struct elektron_data_header header;
  struct elektron_data_sample_slot_header slot_header;
  struct elektron_data_footer footer;
  GByteArray *sample_content, *footer_content;
  guint size, samples_size, payload_size, footer_size, frames;

  sample_info = sample->info;
  frames = MIN (ELEKTRON_SYNTAKT_MAX_SAMPLE_FRAMES, sample_info->frames);
  // TODO: If sample is truncated, the return value should indicate this.
  if (frames < sample_info->frames)
    {
      warn_print ("Sample truncated to %d frames", frames);
    }
  samples_size = frames * sizeof (gint16);
  payload_size = sizeof (struct elektron_data_sample_slot_header) +
    samples_size;
  size = sizeof (struct elektron_data_header) + payload_size;
  sample_content = g_byte_array_sized_new (size);

  header.magic_head = GUINT32_TO_BE (0xac11d303);
  header.header_version = 2;

  // For now, this is only compatible with Syntakt.
  header.family_id = GUINT16_TO_BE (8);
  header.device_id = GUINT16_TO_BE (13);
  header.os_version = GUINT32_TO_BE (0x30303832);
  header.file_type = GUINT32_TO_BE (8);
  header.file_version = 0xffffffff;
  header.file_index = GINT32_TO_BE (slot);
  header.payload_size = GUINT32_TO_BE (payload_size);
  header.is_compressed = 0;
  header.footer_size = sizeof (struct elektron_data_footer);

  slot_header.magic_head = GUINT32_TO_BE (0x53414d50);
  slot_header.version = 0;
  slot_header.length = GINT32_TO_BE (frames);

  gchar *name = elektron_get_cp1252 (sample->name);
  memset (slot_header.name, 0, ELEKTRON_DATA_SAMPLE_MAX_LEN);
  memcpy (slot_header.name, name,
	  MIN (strlen (name), ELEKTRON_DATA_SAMPLE_MAX_LEN));
  g_free (name);

  g_byte_array_append (sample_content, (guint8 *) & header,
		       sizeof (struct elektron_data_header));
  g_byte_array_append (sample_content, (guint8 *) & slot_header,
		       sizeof (struct elektron_data_sample_slot_header));

  /* Convert sample data from host to big-endian byte order for CRC and storage */
  const gint16 *src = (const gint16 *) sample->content->data;
  gint16 *dest = (gint16 *) & sample_content->data[sample_content->len];
  for (guint i = 0; i < frames; i++)
    {
      dest[i] = GINT16_TO_BE (src[i]);
    }
  sample_content->len += samples_size;

  /* CRC includes slot_header + big-endian sample data */
  guint crc_start = sizeof (struct elektron_data_header);
  guint32 crc = elektron_crc ((guint8 *) & sample_content->data[crc_start],
			      payload_size);
  debug_print (1,
	       "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x",
	       payload_size, crc);

  footer_size = sizeof (struct elektron_data_footer);
  footer_content = g_byte_array_sized_new (footer_size);

  footer.hash = GUINT32_TO_BE (crc);
  footer.content_size = GUINT32_TO_BE (footer_size);
  footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa);

  g_byte_array_append (footer_content, (guint8 *) & footer,
		       sizeof (struct elektron_data_footer));

  idata_init (data_sample, sample_content, NULL, NULL, NULL);
  idata_init (data_footer, footer_content, NULL, NULL, NULL);

  return 0;
}

static gint
elektron_download_data_sample (struct backend *backend, const gchar *path,
			       struct idata *sample,
			       struct task_control *control)
{
  gint err;
  struct idata data_sample;

  control->parts = 2;
  control->part = 0;

  err = elektron_download_data_prefix (backend, path, &data_sample, control,
				       FS_DATA_SAMPLES_PREFIX);
  if (err)
    {
      return err;
    }

  err = elektron_set_sample_from_data_sample (sample, &data_sample);
  idata_clear (&data_sample);

  control->part++;
  task_control_set_progress (control, 1.0);

  return err;
}

static gchar *
elektron_get_download_name (struct backend *backend,
			    const struct fs_operations *ops,
			    const gchar *src_path)
{
  gint32 id;
  gint ret;
  gchar *dir, *name;
  struct item_iterator iter;
  struct elektron_iterator_data *data;

  if (ops->id == FS_RAW_ALL || ops->id == FS_RAW_PRESETS)
    {
      return g_path_get_basename (src_path);
    }

  dir = g_path_get_dirname (src_path);
  ret = ops->readdir (backend, &iter, dir, NULL);
  g_free (dir);
  if (ret)
    {
      return NULL;
    }

  name = g_path_get_basename (src_path);
  id = atoi (name);
  g_free (name);

  name = NULL;
  data = iter.data;
  data->load_metadata = FALSE;
  while (!item_iterator_next (&iter))
    {
      if (iter.item.id == id)
	{
	  name = g_strdup (iter.item.name);
	  break;
	}
    }

  item_iterator_free (&iter);

  return name;
}

static gint
elektron_download_pkg (struct backend *backend, const gchar *path,
		       struct idata *output, struct task_control *control,
		       enum elektron_pkg_type type,
		       const struct fs_operations *ops,
		       fs_remote_file_op download)
{
  gint ret;
  gchar *pkg_name;
  struct elektron_pkg pkg;
  struct elektron_data *data = backend->data;

  pkg_name = elektron_get_download_name (backend, ops, path);
  if (!pkg_name)
    {
      return -1;
    }

  if (elektron_pkg_begin (&pkg, pkg_name, backend->version, &data->dev_desc,
			  type))
    {
      g_free (pkg_name);
      return -1;
    }

  ret = elektron_pkg_receive_pkg_resources (&pkg, path, control,
					    backend, download, type);
  ret = ret || elektron_pkg_end (&pkg, output);

  elektron_pkg_destroy (&pkg);
  return ret;
}

static gchar *
elektron_get_upload_path_smplrw (struct backend *backend,
				 const struct fs_operations *ops,
				 const gchar *dst_dir, const gchar *src_path,
				 struct idata *smplrw)
{
  gchar *path, *name, *aux;

  name = g_path_get_basename (src_path);
  filename_remove_ext (name);
  aux = path_chain (PATH_INTERNAL, dst_dir, name);
  g_free (name);

  if (ops->id == FS_RAW_ALL || ops->id == FS_RAW_PRESETS)
    {
      path = elektron_mc_add_snd_ext (aux);
      g_free (aux);
    }
  else
    {
      path = aux;
    }
  return path;
}

// As Elektron devices provide their own file extension and the file content is
// not just SysEx, it is not needed to indicate in the filename the type of
// device, filesystem or id.

static gchar *
elektron_get_download_path (struct backend *backend,
			    const struct fs_operations *ops,
			    const gchar *dst_dir, const gchar *src_path,
			    struct idata *any)
{
  gchar *path, *name, *src_fpath;
  const gchar *md_ext, *ext = filename_get_ext (src_path);

  // Examples:
  // 0:/project0
  // 0:/soundbanks/A/1
  // 0:/soundbanks/A/1/.metadata

  if (strcmp (ext, FS_DATA_METADATA_EXT))
    {
      src_fpath = strdup (src_path);
      md_ext = "";
    }
  else
    {
      src_fpath = g_path_get_dirname (src_path);
      md_ext = FS_DATA_METADATA_FILE;
    }

  name = elektron_get_download_name (backend, ops, src_fpath);
  g_free (src_fpath);
  if (name)
    {
      GString *filename = g_string_new (NULL);
      const gchar *dl_ext = ops->get_exts (backend, ops)[0];
      g_string_append_printf (filename, "%s.%s%s", name, dl_ext, md_ext);
      path = path_chain (PATH_SYSTEM, dst_dir, filename->str);
      g_free (name);
      g_string_free (filename, TRUE);
    }
  else
    {
      path = NULL;
    }

  return path;
}

static gint
elektron_upload_data_list_prefix (struct backend *backend, const gchar *path,
				  GSList *list, struct task_control *control,
				  const gchar *prefix)
{
  gint err;
  guint id;
  GSList *iter;
  gboolean active;
  struct idata *data;
  gchar *path_w_prefix;
  GByteArray *rx_msg, *tx_msg, *content;
  guint32 seq, jid, jidbe, crc, len, r_jid, r_seq, offset, *data32,
    aux32, total, full_content_size, full_content_sent;

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  full_content_size = 0;
  for (iter = list; iter != NULL; iter = iter->next)
    {
      data = iter->data;
      content = data->content;
      full_content_size += content->len;
    }

  path_w_prefix = elektron_add_prefix_to_path (path, prefix);
  err = elektron_open_datum (backend, path_w_prefix, &jid, O_WRONLY,
			     full_content_size);
  g_free (path_w_prefix);
  if (err)
    {
      goto end;
    }

  g_usleep (BE_REST_TIME_US);

  jidbe = g_htonl (jid);

  seq = 0;
  full_content_sent = 0;
  for (iter = list; iter != NULL; iter = iter->next)
    {
      data = iter->data;
      content = data->content;

      offset = 0;

      active = controllable_is_active (&control->controllable);

      while (offset < content->len && active)
	{
	  tx_msg = elektron_new_msg (DATA_WRITE_PARTIAL_REQUEST,
				     sizeof (DATA_WRITE_PARTIAL_REQUEST));
	  g_byte_array_append (tx_msg, (guint8 *) & jidbe, sizeof (guint32));
	  aux32 = g_htonl (seq);
	  g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32));

	  if (offset + DATA_TRANSF_BLOCK_BYTES < content->len)
	    {
	      len = DATA_TRANSF_BLOCK_BYTES;
	    }
	  else
	    {
	      len = content->len - offset;
	    }

	  crc = elektron_crc (&content->data[offset], len);
	  aux32 = g_htonl (crc);
	  g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32));

	  aux32 = g_htonl (len);
	  g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32));

	  g_byte_array_append (tx_msg, &content->data[offset], len);

	  rx_msg = elektron_tx_and_rx (backend, tx_msg,
				       &control->controllable);
	  if (!rx_msg)
	    {
	      err = -EIO;
	      goto end;
	    }

	  g_usleep (BE_REST_TIME_US);

	  if (!elektron_get_msg_status (rx_msg))
	    {
	      err = -EPERM;
	      error_print ("%s (%s)", backend_strerror (backend, err),
			   elektron_get_msg_string (rx_msg));
	      free_msg (rx_msg);
	      break;
	    }

	  data32 = (guint32 *) & rx_msg->data[6];
	  r_jid = g_ntohl (*data32);

	  data32 = (guint32 *) & rx_msg->data[10];
	  r_seq = g_ntohl (*data32);

	  data32 = (guint32 *) & rx_msg->data[14];
	  total = g_ntohl (*data32);

	  free_msg (rx_msg);

	  debug_print (1,
		       "Write datum info: job id: %d; seq: %d; total: %d",
		       r_jid, r_seq, total);

	  seq++;
	  offset += len;
	  full_content_sent += len;

	  if (total != full_content_sent)
	    {
	      error_print
		("Actual upload bytes (%d) differs from expected ones (%d)",
		 total, full_content_sent);
	    }

	  gdouble progress = full_content_sent / (gdouble) full_content_size;
	  task_control_set_progress (control, progress);

	  active = controllable_is_active (&control->controllable);
	}

      debug_print (2, "Partial request with %d bytes sent", offset);
    }

  debug_print (2, "%d bytes sent", full_content_sent);

  err = elektron_close_datum (backend, jid, O_WRONLY, full_content_size);

end:
  return err;
}

static gint
elektron_upload_data_prefix (struct backend *backend, const gchar *path,
			     struct idata *data,
			     struct task_control *control,
			     const gchar *prefix)
{
  gint err;
  GSList *list;

  list = g_slist_append (NULL, data);
  err = elektron_upload_data_list_prefix (backend, path, list, control,
					  prefix);
  g_slist_free (list);

  return err;
}

static gint
elektron_upload_data_any (struct backend *backend, const gchar *path,
			  struct idata *any, struct task_control *control)
{
  control->parts = 1;
  control->part = 0;
  return elektron_upload_data_prefix (backend, path, any, control, NULL);
}

static gint
elektron_upload_data_prj (struct backend *backend, const gchar *path,
			  struct idata *prj, struct task_control *control)
{
  return elektron_upload_data_prefix (backend, path, prj, control,
				      FS_DATA_PRJ_PREFIX);
}

static gint
elektron_upload_data_snd (struct backend *backend, const gchar *path,
			  struct idata *snd, struct task_control *control)
{
  return elektron_upload_data_prefix (backend, path, snd, control,
				      FS_DATA_SND_PREFIX);
}

static gint
elektron_upload_data_pst (struct backend *backend, const gchar *path,
			  struct idata *pst, struct task_control *control)
{
  return elektron_upload_data_prefix (backend, path, pst, control,
				      FS_DATA_PST_PREFIX);
}

static gint
elektron_upload_data_sample (struct backend *backend, const gchar *path,
			     struct idata *sample,
			     struct task_control *control)
{
  gint err;
  guint id;
  GSList *list;
  struct idata data_sample, data_footer;

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  control->parts = 2;
  control->part = 0;

  err = elektron_set_data_sample_from_sample (&data_sample, &data_footer,
					      sample, id);
  if (err)
    {
      return err;
    }

  task_control_set_progress (control, 1.0);
  control->part++;

  list = g_slist_append (NULL, &data_sample);
  list = g_slist_append (list, &data_footer);
  err = elektron_upload_data_list_prefix (backend, path, list, control,
					  FS_DATA_SAMPLES_PREFIX);
  g_slist_free (list);
  idata_clear (&data_sample);
  idata_clear (&data_footer);

  return err;
}

static gint
elektron_upload_pkg (struct backend *backend, const gchar *path,
		     struct idata *input, struct task_control *control,
		     guint8 type, const struct fs_operations *ops,
		     fs_remote_file_op upload)
{
  gint ret;
  struct elektron_pkg pkg;
  struct elektron_data *data = backend->data;

  ret = elektron_pkg_open (&pkg, input, &data->dev_desc);
  if (!ret)
    {
      ret =
	elektron_pkg_send_pkg_resources (&pkg, path, control, backend,
					 upload);
      elektron_pkg_close (&pkg);
    }
  return ret;
}

const gchar **
elektron_get_dev_exts (struct backend *backend,
		       const struct fs_operations *ops)
{
  struct elektron_data *data = backend->data;
  struct elektron_fs_desc *fs_desc = data->dev_desc.fs_descs;

  for (guint i = 0; i < data->dev_desc.fs_descs_len; i++)
    {
      if (!strcmp (fs_desc->name, ops->name))
	{
	  return (const gchar **) fs_desc->extensions;
	}
      fs_desc++;
    }

  return NULL;
}

const gchar **
elektron_get_data_any_exts (struct backend *backend,
			    const struct fs_operations *ops)
{
  return FS_DATA_ANY_EXTS;
}

const gchar **
elektron_get_raw_any_exts (struct backend *backend,
			   const struct fs_operations *ops)
{
  return FS_RAW_ANY_EXTS;
}

gint
elektron_sample_load (struct backend *backend, const gchar *path,
		      struct idata *sample, struct task_control *control)
{
  gint err, target_channels;
  struct sample_info sample_info;
  struct elektron_data *data = backend->data;

  if (data->dev_desc.id == ELEKTRON_DIGITAKT_II_ID)
    {
      sample_info_init (&sample_info);
      err = sample_load_sample_info (path, &sample_info);
      if (err)
	{
	  return err;
	}

      target_channels = sample_info.channels == 2 ? 2 : 1;
      sample_info_clear (&sample_info);
    }
  else
    {
      target_channels = 1;
    }

  return common_sample_load (path, sample, control, target_channels,
			     ELEKTRON_SAMPLE_RATE, SF_FORMAT_PCM_16, FALSE);
}

gchar *
elektron_get_sample_path_from_hash_size (struct backend *backend,
					 guint32 hash, guint32 size)
{
  guint32 aux32;
  gchar *path;
  GByteArray *rx_msg, *tx_msg =
    elektron_new_msg (FS_SAMPLE_GET_FILE_INFO_FROM_HASH_AND_SIZE_REQUEST,
		      sizeof
		      (FS_SAMPLE_GET_FILE_INFO_FROM_HASH_AND_SIZE_REQUEST));

  aux32 = g_htonl (hash);
  memcpy (&tx_msg->data[5], &aux32, sizeof (guint32));
  aux32 = g_htonl (size);
  memcpy (&tx_msg->data[9], &aux32, sizeof (guint32));

  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return NULL;
    }

  if (elektron_get_msg_status (rx_msg))
    {
      path = strdup ((gchar *) & rx_msg->data[14]);
    }
  else
    {
      path = NULL;
    }
  g_byte_array_free (rx_msg, TRUE);
  return path;
}

static gint
elektron_sample_save (struct backend *backend, const gchar *path,
		      struct idata *sample, struct task_control *control)
{
  return sample_save_to_file (path, sample, control,
			      SF_FORMAT_WAV | SF_FORMAT_PCM_16);
}

static gint
elektron_ram_slot_next_entry (struct item_iterator *iter)
{
  guint16 id;
  guint8 used;
  guint32 hash, size;
  struct elektron_iterator_data *data = iter->data;

  if (iter->item.id + 1 >= data->max_slots)
    {
      return -ENOENT;
    }

  memcpy (&hash, &data->msg->data[data->pos], sizeof (guint32));
  hash = GUINT32_FROM_BE (hash);
  data->pos += sizeof (guint32);
  memcpy (&size, &data->msg->data[data->pos], sizeof (guint32));
  size = GUINT32_FROM_BE (size);
  data->pos += sizeof (guint32);
  memcpy (&id, &data->msg->data[data->pos], sizeof (guint16));
  id = GUINT16_FROM_BE (id);
  data->pos += sizeof (guint16);
  used = data->msg->data[data->pos];
  data->pos += sizeof (guint8);

  iter->item.id = id;
  elektron_set_slot (&iter->item);
  iter->item.type = ITEM_TYPE_FILE;
  item_set_object_info (&iter->item, "%s",
			used ? ELEKTRON_RAM_SLOT_USED : "");
  if (size == 0)
    {
      item_set_name (&iter->item, "");
      iter->item.size = -1;
      iter->item.type = ITEM_TYPE_FILE;
    }
  else
    {
      gchar *path_cp1252;
      path_cp1252 = elektron_get_sample_path_from_hash_size (data->backend,
							     hash, size);
      if (path_cp1252)
	{
	  elektron_item_set_name (&iter->item, path_cp1252);
	  iter->item.size = size;
	  iter->item.type = ITEM_TYPE_FILE;
	}
      else
	{
	  item_set_name (&iter->item, "");
	  iter->item.size = -1;
	  iter->item.type = ITEM_TYPE_FILE;
	}
    }

  return 0;
}

static gint
elektron_ram_read_dir (struct backend *backend,
		       struct item_iterator *iter, const gchar *dir,
		       const gchar **extensions)
{
  gint err;
  guint32 last_ram_slots;
  GByteArray *tx_msg, *rx_msg;

  if (strcmp (dir, "/"))
    {
      return -ENOTDIR;
    }

  tx_msg = elektron_new_msg (DIGITAKT_RAM_READ_REQUEST,
			     sizeof (DIGITAKT_RAM_READ_REQUEST));
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  memcpy (&last_ram_slots, &rx_msg->data[5], sizeof (guint32));
  last_ram_slots = GUINT32_FROM_BE (last_ram_slots);
  err = elektron_init_iterator (backend, iter, dir, rx_msg,
				elektron_ram_slot_next_entry,
				ITER_MODE_RAM_SLOTS,
				FS_RAM_SLOTS_START_POS + sizeof (guint32),
				last_ram_slots + 1);
  // Item 0 is always OFF and is not included in the message
  iter->item.id = 1;

  return err;
}

static gint
elektron_ram_clear_sample (struct backend *backend, const gchar *path)
{
  gint err;
  guint id;
  GByteArray *tx_msg, *rx_msg;

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  tx_msg = elektron_new_msg (DIGITAKT_RAM_CLEAR_SLOT_REQUEST,
			     sizeof (DIGITAKT_RAM_CLEAR_SLOT_REQUEST));
  tx_msg->data[9] = id;
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  g_usleep (BE_REST_TIME_US);

  return 0;
}

static gint
elektron_ram_download_sample (struct backend *backend,
			      const gchar *path,
			      struct idata *sample,
			      struct task_control *control)
{
  gint err;
  guint id;
  struct item_iterator iter;
  gchar sample_path[ITEM_NAME_MAX];

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  err = elektron_ram_read_dir (backend, &iter, "/", NULL);
  if (err)
    {
      return err;
    }

  sample_path[0] = 0;
  while (!item_iterator_next (&iter))
    {
      if (id == iter.item.id)
	{
	  strncpy (sample_path, iter.item.name, ITEM_NAME_MAX);
	  break;
	}
    }

  item_iterator_free (&iter);

  g_usleep (BE_REST_TIME_US);

  if (sample_path[0] == 0)
    {
      return -EINVAL;
    }

  control->parts = 1;
  control->part = 0;

  return elektron_download_sample_part (backend, sample_path, sample,
					control);
}

static gchar *
elektron_ram_get_download_path (struct backend *backend,
				const struct fs_operations *ops,
				const gchar *dst_dir,
				const gchar *src_path, struct idata *idata)
{
  const gchar *ext = GET_SAVE_EXT (ops, backend);
  gchar *name = g_path_get_basename (idata->name);
  GString *name_with_ext = g_string_new (NULL);
  g_string_append_printf (name_with_ext, "%s.%s", name, ext);
  gchar *path = path_chain (PATH_SYSTEM, dst_dir, name_with_ext->str);
  g_string_free (name_with_ext, TRUE);
  g_free (name);
  return path;
}

static gint
elektron_ram_set_sample (struct backend *backend,
			 const gchar *path, guint16 ram_slot,
			 guint8 track, struct task_control *control)
{
  GByteArray *tx_msg, *rx_msg;
  guint16 ram_slot_be = GUINT16_TO_BE (ram_slot);

  tx_msg = elektron_new_msg_path (DIGITAKT_RAM_LOAD_SLOT_REQUEST,
				  sizeof
				  (DIGITAKT_RAM_LOAD_SLOT_REQUEST), path);
  g_byte_array_append (tx_msg, (guint8 *) & track, 1);	// 0xff ignores the track and loads to RAM only.
  g_byte_array_append (tx_msg, (guint8 *) & ram_slot_be, 2);
  g_byte_array_append (tx_msg, (guint8 *) "\x01", 1);	// Unknown. Required value.
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);

  // It seems elektron_get_msg_status(rx_msg) == 1.

  // The response has 5 bytes.
  // First byte is 0x01. Last byte looks like a sequence increased in every response.

  if (!rx_msg)
    {
      return -EIO;
    }

  free_msg (rx_msg);

  // It takes a while for the result to be available
  g_usleep (500000);
  task_control_set_progress (control, 0.5);
  g_usleep (500000);
  task_control_set_progress (control, 1.0);

  return 0;
}

static gint
elektron_ram_track_upload (struct backend *backend, guint16 ram_slot,
			   guint8 track, struct idata *sample,
			   struct task_control *control)
{
  gint err;
  gchar *upload_path;

  upload_path = path_chain (PATH_SYSTEM, RAM_OP_DEF_DIR, sample->name);
  err = elektron_upload_sample_part (backend, upload_path, sample, control);
  if (err)
    {
      goto end;
    }

  control->part++;

  err = elektron_ram_set_sample (backend, upload_path, ram_slot,
				 track, control);

end:
  g_free (upload_path);
  return err;
}

static gint
elektron_ram_upload_sample (struct backend *backend,
			    const gchar *path, struct idata *sample,
			    struct task_control *control)
{
  gint err;
  guint id;

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  control->parts = 2;
  control->part = 0;

  return elektron_ram_track_upload (backend, id, 0xff, sample, control);
}

gint
elektron_ram_clear_unused_slots (struct backend *backend)
{
  gint err;
  struct item_iterator iter;
  gchar path[PATH_MAX];

  err = elektron_ram_read_dir (backend, &iter, "/", NULL);
  if (err)
    {
      return err;
    }

  while (!item_iterator_next (&iter))
    {
      if (iter.item.size != 0 && !strcmp (iter.item.object_info, ""))
	{
	  snprintf (path, PATH_MAX, "/%d", iter.item.id);
	  debug_print (1, "Clearing RAM slot '%s'...", path);
	  elektron_ram_clear_sample (backend, path);
	}
    }

  item_iterator_free (&iter);

  return 0;
}

static gint
elektron_ram_track_next_entry (struct item_iterator *iter)
{
  guint16 slot;
  guint32 hash, size;
  struct elektron_iterator_data *data = iter->data;

  if (iter->item.id + 1 >= data->max_slots)
    {
      return -ENOENT;
    }

  memcpy (&hash, &data->msg->data[data->pos], sizeof (guint32));
  hash = GUINT32_FROM_BE (hash);
  data->pos += sizeof (guint32);
  memcpy (&size, &data->msg->data[data->pos], sizeof (guint32));
  size = GUINT32_FROM_BE (size);
  data->pos += sizeof (guint32);
  memcpy (&slot, &data->msg->data[data->pos], sizeof (guint16));
  slot = GUINT16_FROM_BE (slot);
  data->pos += sizeof (guint16);

  iter->item.id++;
  elektron_set_slot (&iter->item);
  iter->item.type = ITEM_TYPE_FILE;
  iter->item.size = -1;
  item_set_name (&iter->item, "%d", iter->item.id + 1);
  if (slot)
    {
      gchar *path_cp1252;
      gchar *path_utf8;
      path_cp1252 = elektron_get_sample_path_from_hash_size (data->backend,
							     hash, size);
      if (path_cp1252)
	{
	  path_utf8 = elektron_name_to_utf8 (path_cp1252);
	}
      else
	{
	  path_utf8 = NULL;
	}
      item_set_object_info (&iter->item, "slot=%d:path=%s", slot,
			    path_utf8 ? path_utf8 : "");
      g_free (path_cp1252);
      g_free (path_utf8);
    }
  else
    {
      item_set_object_info (&iter->item, "");
    }

  return 0;
}

static gint
elektron_digitakt_track_read_dir (struct backend *backend,
				  struct item_iterator *iter,
				  const gchar *dir, const gchar **extensions)
{
  gint err;
  GByteArray *tx_msg, *rx_msg;
  guint32 ram_slots, ram_tracks, ram_tracks_pos;

  if (strcmp (dir, "/"))
    {
      return -ENOTDIR;
    }

  tx_msg = elektron_new_msg (DIGITAKT_RAM_READ_REQUEST,
			     sizeof (DIGITAKT_RAM_READ_REQUEST));
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  memcpy (&ram_slots, &rx_msg->data[FS_RAM_SLOTS_START_POS],
	  sizeof (guint32));
  ram_slots = GUINT32_FROM_BE (ram_slots);
  ram_tracks_pos = FS_RAM_SLOTS_START_POS + sizeof (guint32) + ram_slots * 11;
  memcpy (&ram_tracks, &rx_msg->data[ram_tracks_pos], sizeof (guint32));
  ram_tracks = GUINT32_FROM_BE (ram_tracks);
  err = elektron_init_iterator (backend, iter, dir, rx_msg,
				elektron_ram_track_next_entry,
				ITER_MODE_RAM_TRACKS,
				ram_tracks_pos + sizeof (guint32),
				ram_tracks);
  iter->item.id = -1;

  return err;
}

static gint
elektron_ram_get_free_slot (struct backend *backend)
{
  gint err;
  gint free_slot = -ENOMEM;
  struct item_iterator iter;

  err = elektron_ram_read_dir (backend, &iter, "/", NULL);
  if (err)
    {
      return err;
    }

  while (!item_iterator_next (&iter))
    {
      if (iter.item.size == -1)
	{
	  free_slot = iter.item.id;
	  break;
	}
    }

  item_iterator_free (&iter);

  return free_slot;
}

static gint
elektron_digitakt_track_upload_sample_int (struct backend *backend,
					   const gchar *path,
					   struct idata *sample,
					   struct task_control *control)
{
  gint err;
  guint id;
  gint free_slot;

  free_slot = elektron_ram_get_free_slot (backend);
  if (free_slot < 1)
    {
      return free_slot;
    }

  err = common_slot_get_id_from_path (path, &id);
  if (err)
    {
      return err;
    }

  return elektron_ram_track_upload (backend, free_slot, id, sample, control);
}

static gint
elektron_digitakt_track_upload_sample (struct backend *backend,
				       const gchar *path,
				       struct idata *sample,
				       struct task_control *control)
{
  control->parts = 2;
  control->part = 0;

  return elektron_digitakt_track_upload_sample_int (backend, path, sample,
						    control);
}

static gint
elektron_digitakt_track_loop_upload_sample (struct backend *backend,
					    const gchar *path,
					    struct idata *sample,
					    struct task_control *control)
{
  guint32 t;
  guint8 mode;
  gdouble tempo;
  GByteArray *tx_msg, *rx_msg;
  struct sample_info *sample_info;

  sample_info = sample->info;

  tx_msg = elektron_new_msg (DIGITAKT_GET_PATTERN_INFO,
			     sizeof (DIGITAKT_GET_PATTERN_INFO));
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      return -EIO;
    }

  mode = elektron_get_msg_status (rx_msg);	// This is the tempo mode. It's not the status.

  if (mode)
    {
      memcpy (&t, &rx_msg->data[6], sizeof (guint32));
    }
  else
    {
      memcpy (&t, &rx_msg->data[10], sizeof (guint32));
    }
  t = GUINT32_FROM_BE (t);
  tempo = t / 120.0;

  debug_print (2, "%s tempo: %.2f BPM", mode ? "Pattern" : "Global", tempo);

  free_msg (rx_msg);

  if (sample_info->tempo == 0)
    {
      control->parts = 2;
      control->part = 0;

      debug_print (1, "No sample tempo. Performing direct upload...");

      return elektron_digitakt_track_upload_sample_int (backend, path, sample,
							control);
    }
  else
    {
      gint err;
      gdouble ratio = sample_info->tempo / (gdouble) tempo;

      debug_print (1,
		   "Sample tempo is %.2f BPM. Performing timestretched upload...",
		   sample_info->tempo);

      control->parts = 3;
      control->part = 0;

      err = sample_ops_timestretch (sample, ratio);
      if (err)
	{
	  return err;
	}

      control->part++;

      err = elektron_digitakt_track_upload_sample_int (backend, path,
						       sample, control);

      return err;
    }
}

static const struct fs_operations FS_SAMPLES_OPERATIONS = {
  .id = FS_SAMPLES,
  .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO |
    FS_OPTION_SHOW_SIZE_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "sample",
  .gui_name = "Samples",
  .gui_icon = FS_ICON_WAVE,
  .file_icon = FS_ICON_WAVE,
  .max_name_len = ELEKTRON_NAME_MAX_LEN,
  .readdir = elektron_read_samples_dir,
  .file_exists = elektron_sample_file_exists,
  .print_item = elektron_print_smplrw,
  .mkdir = elektron_create_samples_dir,
  .delete = elektron_delete_samples_item,
  .rename = elektron_move_samples_item,
  .move = elektron_move_samples_item,
  .download = elektron_download_sample,
  .upload = elektron_upload_sample,
  .load = elektron_sample_load,
  .save = elektron_sample_save,
  .get_exts = sample_get_sample_extensions,
  .get_upload_path = elektron_get_upload_path_smplrw,
  .get_download_path = common_system_get_download_path
};

static const struct fs_operations FS_RAW_ANY_OPERATIONS = {
  .id = FS_RAW_ALL,
  .options = 0,
  .name = "raw",
  .max_name_len = ELEKTRON_NAME_MAX_LEN,
  .readdir = elektron_read_raw_dir,
  .file_exists = elektron_raw_file_exists,
  .print_item = elektron_print_smplrw,
  .mkdir = elektron_create_raw_dir,
  .delete = elektron_delete_raw_item,
  .rename = elektron_move_raw_item,
  .move = elektron_move_raw_item,
  .download = elektron_download_raw,
  .upload = elektron_upload_raw,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_raw_any_exts,
  .get_upload_path = elektron_get_upload_path_smplrw,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_RAW_PRESETS_OPERATIONS = {
  .id = FS_RAW_PRESETS,
  .options = FS_OPTION_SHOW_SIZE_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "preset-raw",
  .gui_name = "Presets",
  .gui_icon = FS_ICON_PRESET,
  .file_icon = FS_ICON_PRESET,
  .max_name_len = ELEKTRON_NAME_MAX_LEN,
  .readdir = elektron_read_raw_dir,
  .file_exists = elektron_raw_file_exists,
  .print_item = elektron_print_smplrw,
  .mkdir = elektron_create_raw_dir,
  .delete = elektron_delete_raw_item,
  .rename = elektron_move_raw_item,
  .move = elektron_move_raw_item,
  .download = elektron_download_raw_pst_pkg,
  .upload = elektron_upload_raw_pst_pkg,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_dev_exts,
  .get_upload_path = elektron_get_upload_path_smplrw,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_ANY_OPERATIONS = {
  .id = FS_DATA_ANY,
  .options = FS_OPTION_SLOT_STORAGE,
  .name = "data",
  .readdir = elektron_read_data_dir_any,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_any,
  .move = elektron_move_data_item_any,
  .copy = elektron_copy_data_item_any,
  .swap = elektron_swap_data_item_any,
  .download = elektron_download_data_any,
  .upload = elektron_upload_data_any,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_data_any_exts,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_PRJ_OPERATIONS = {
  .id = FS_DATA_PRJ,
  .options = FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN |
    FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "project",
  .gui_name = "Projects",
  .gui_icon = FS_ICON_PROJECT,
  .file_icon = FS_ICON_PROJECT,
  .readdir = elektron_read_data_dir_prj,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_prj,
  .move = elektron_move_data_item_prj,
  .copy = elektron_copy_data_item_prj,
  .swap = elektron_swap_data_item_prj,
  .download = elektron_download_data_prj_pkg,
  .upload = elektron_upload_data_prj_pkg,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_dev_exts,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_SND_OPERATIONS = {
  .id = FS_DATA_SND,
  .options = FS_OPTION_SINGLE_OP | FS_OPTION_SLOT_STORAGE |
    FS_OPTION_SHOW_SIZE_COLUMN | FS_OPTION_SHOW_SLOT_COLUMN |
    FS_OPTION_SHOW_INFO_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "sound",
  .gui_name = "Sounds",
  .gui_icon = FS_ICON_PRESET,
  .file_icon = FS_ICON_PRESET,
  .readdir = elektron_read_data_dir_snd,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_snd,
  .move = elektron_move_data_item_snd,
  .copy = elektron_copy_data_item_snd,
  .swap = elektron_swap_data_item_snd,
  .download = elektron_download_data_snd_pkg,
  .upload = elektron_upload_data_snd_pkg,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_dev_exts,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_PST_OPERATIONS = {
  .id = FS_DATA_PST,
  .options = FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN |
    FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "preset",
  .gui_name = "Presets",
  .gui_icon = FS_ICON_PRESET,
  .file_icon = FS_ICON_PRESET,
  .readdir = elektron_read_data_dir_pst,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_pst,
  .move = elektron_move_data_item_pst,
  .copy = elektron_copy_data_item_pst,
  .swap = elektron_swap_data_item_pst,
  .download = elektron_download_data_pst_pkg,
  .upload = elektron_upload_data_pst_pkg,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_dev_exts,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_TAKT_II_PST_OPERATIONS = {
  .id = FS_DATA_TAKT_II_PST,
  .options = FS_OPTION_SINGLE_OP | FS_OPTION_SLOT_STORAGE |
    FS_OPTION_SHOW_SIZE_COLUMN | FS_OPTION_SHOW_SLOT_COLUMN |
    FS_OPTION_SHOW_INFO_COLUMN | FS_OPTION_ALLOW_SEARCH,
  .name = "preset-takt-ii",
  .gui_name = "Presets",
  .gui_icon = FS_ICON_PRESET,
  .file_icon = FS_ICON_PRESET,
  .readdir = elektron_read_data_dir_snd,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_snd,
  .move = elektron_move_data_item_snd,
  .copy = elektron_copy_data_item_snd,
  .swap = elektron_swap_data_item_snd,
  .download = elektron_download_data_snd_pkg,
  .upload = elektron_upload_data_snd_pkg,
  .load = common_file_load,
  .save = common_file_save,
  .get_exts = elektron_get_dev_exts,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_get_download_path
};

static const struct fs_operations FS_DATA_SAMPLES_OPERATIONS = {
  .id = FS_DATA_SAMPLES,
  .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO |
    FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN |
    FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_SHOW_INFO_COLUMN |
    FS_OPTION_ALLOW_SEARCH,
  .name = "data-sample",
  .gui_name = "Samples",
  .gui_icon = FS_ICON_WAVE,
  .file_icon = FS_ICON_WAVE,
  .readdir = elektron_read_data_dir_sample,
  .print_item = elektron_print_data,
  .delete = elektron_clear_data_item_sample,
  .move = elektron_move_data_item_sample,
  .copy = elektron_copy_data_item_sample,
  .swap = elektron_swap_data_item_sample,
  .download = elektron_download_data_sample,
  .upload = elektron_upload_data_sample,
  .load = elektron_sample_load,
  .save = elektron_sample_save,
  .get_exts = sample_get_sample_extensions,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = common_system_get_download_path
};

static const struct fs_operations FS_DIGITAKT_RAM_OPERATIONS = {
  .id = FS_DIGITAKT_RAM,
  .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO |
    FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN |
    FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_SHOW_INFO_COLUMN |
    FS_OPTION_ALLOW_SEARCH,
  .name = "ram",
  .gui_name = "RAM",
  .gui_icon = FS_ICON_CHIP,
  .file_icon = FS_ICON_WAVE,
  .readdir = elektron_ram_read_dir,
  .delete = elektron_ram_clear_sample,
  .download = elektron_ram_download_sample,
  .upload = elektron_ram_upload_sample,
  .load = elektron_sample_load,
  .save = elektron_sample_save,
  .get_exts = sample_get_sample_extensions,
  .get_upload_path = common_slot_get_upload_path,
  .get_download_path = elektron_ram_get_download_path
};

static const struct fs_operations FS_DIGITAKT_TRACK_OPERATIONS = {
  .id = FS_DIGITAKT_TRACK,
  .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO |
    FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_INFO_COLUMN,
  .name = "track",
  .gui_name = "Tracks",
  .gui_icon = FS_ICON_TRACK,
  .file_icon = FS_ICON_WAVE,
  .readdir = elektron_digitakt_track_read_dir,
  .upload = elektron_digitakt_track_upload_sample,
  .load = elektron_sample_load,
  .get_exts = sample_get_sample_extensions,
  .get_upload_path = common_slot_get_upload_path
};

static const struct fs_operations FS_DIGITAKT_TRACK_LOOP_OPERATIONS = {
  .id = FS_DIGITAKT_TRACK_LOOP,
  .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO |
    FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_INFO_COLUMN,
  .name = "track-loop",
  .gui_name = "Tracks (loop)",
  .gui_icon = FS_ICON_TRACK_LOOP,
  .file_icon = FS_ICON_WAVE,
  .readdir = elektron_digitakt_track_read_dir,
  .upload = elektron_digitakt_track_loop_upload_sample,
  .load = elektron_sample_load,
  .get_exts = sample_get_sample_extensions,
  .get_upload_path = common_slot_get_upload_path
};

static const struct fs_operations *FS_OPERATIONS[] = {
  &FS_SAMPLES_OPERATIONS, &FS_RAW_ANY_OPERATIONS, &FS_RAW_PRESETS_OPERATIONS,
  &FS_DATA_ANY_OPERATIONS, &FS_DATA_PRJ_OPERATIONS, &FS_DATA_SND_OPERATIONS,
  &FS_DATA_PST_OPERATIONS, &FS_DATA_TAKT_II_PST_OPERATIONS,
  &FS_DATA_SAMPLES_OPERATIONS, &FS_DIGITAKT_RAM_OPERATIONS,
  &FS_DIGITAKT_TRACK_OPERATIONS, &FS_DIGITAKT_TRACK_LOOP_OPERATIONS, NULL
};

gint
elektron_configure_device_from_file (struct backend *backend, guint8 id,
				     const gchar *filename)
{
  gint err, devices, storage;
  JsonParser *parser;
  JsonReader *reader;
  gchar **members;
  gchar **name;
  GError *error = NULL;
  struct elektron_data *data = backend->data;

  parser = json_parser_new ();

  if (!json_parser_load_from_file (parser, filename, &error))
    {
      debug_print (1, "%s", error->message);
      g_clear_error (&error);
      err = -ENODEV;
      goto cleanup_parser;
    }

  debug_print (1, "Parsing %s...", filename);

  reader = json_reader_new (json_parser_get_root (parser));
  if (!reader)
    {
      error_print ("Unable to read from parser");
      err = -ENODEV;
      goto cleanup_parser;
    }

  if (!json_reader_is_array (reader))
    {
      error_print ("Not an array");
      err = -ENODEV;
      goto cleanup_reader;
    }

  devices = json_reader_count_elements (reader);
  if (!devices)
    {
      debug_print (1, "No devices found");
      err = -ENODEV;
      goto cleanup_reader;
    }

  err = -ENODEV;
  for (guint i = 0; i < devices; i++)
    {
      if (!json_reader_read_element (reader, i))
	{
	  error_print ("Cannot read element %d. Continuing...", i);
	  continue;
	}

      if (!json_reader_read_member (reader, DEV_TAG_ID))
	{
	  error_print ("Cannot read member '%s'. Continuing...", DEV_TAG_ID);
	  continue;
	}
      data->dev_desc.id = json_reader_get_int_value (reader);
      json_reader_end_member (reader);

      if (data->dev_desc.id != id)
	{
	  json_reader_end_element (reader);
	  continue;
	}

      err = 0;
      debug_print (1, "Device %d found", id);

      if (!json_reader_read_member (reader, DEV_TAG_NAME))
	{
	  error_print ("Cannot read member '%s'. Stopping...", DEV_TAG_NAME);
	  json_reader_end_element (reader);
	  err = -ENODEV;
	  break;
	}
      snprintf (backend->name, LABEL_MAX, "%s",
		json_reader_get_string_value (reader));
      json_reader_end_member (reader);

      if (!json_reader_read_member (reader, DEV_TAG_FILESYSTEMS))
	{
	  error_print ("Cannot read member '%s'. Stopping...",
		       DEV_TAG_FILESYSTEMS);
	  json_reader_end_element (reader);
	  err = -ENODEV;
	  break;
	}
      members = json_reader_list_members (reader);
      name = members;
      data->dev_desc.fs_descs_len = 0;
      backend->fs_ops = NULL;
      while (*name)
	{
	  const struct fs_operations **fs_ops = FS_OPERATIONS;
	  while (*fs_ops)
	    {
	      const gchar *fs_name = (*fs_ops)->name;
	      if (!strcmp (fs_name, *name))
		{
		  debug_print (2, "Reading '%s' filesystem...", fs_name);
		  backend->fs_ops = g_slist_append (backend->fs_ops,
						    (gpointer) * fs_ops);
		  break;
		}
	      fs_ops++;
	    }

	  if (*fs_ops)
	    {
	      if (data->dev_desc.fs_descs_len == ELEKTRON_MAX_FS)
		{
		  error_print ("Too many filesystems");
		  break;
		}
	      struct elektron_fs_desc *fs_desc =
		&data->dev_desc.fs_descs[data->dev_desc.fs_descs_len];
	      snprintf (fs_desc->name, LABEL_MAX, "%s", *name);
	      data->dev_desc.fs_descs_len++;

	      json_reader_read_member (reader, *name);

	      gint j = 0;

	      if (!json_reader_get_null_value (reader))
		{
		  gint extensions = json_reader_count_elements (reader);
		  for (; j < extensions && j < ELEKTRON_MAX_EXTENSIONS - 1;
		       j++)
		    {
		      json_reader_read_element (reader, j);

		      const gchar *ext_name =
			json_reader_get_string_value (reader);
		      debug_print (2, "Adding '%s' extension...", ext_name);
		      fs_desc->extensions[j] = strdup (ext_name);

		      json_reader_end_element (reader);
		    }
		}

	      fs_desc->extensions[j] = NULL;

	      json_reader_end_member (reader);
	    }
	  else
	    {
	      error_print ("Filesystem '%s' not found", *name);
	    }

	  name++;
	}

      g_strfreev (members);
      json_reader_end_member (reader);

      if (!json_reader_read_member (reader, DEV_TAG_STORAGE))
	{
	  error_print ("Cannot read member '%s'. Stopping...",
		       DEV_TAG_STORAGE);
	  json_reader_end_element (reader);
	  err = -ENODEV;
	  break;
	}
      data->dev_desc.storage = 0;
      storage = json_reader_count_elements (reader);
      if (storage > ELEKTRON_MAX_STORAGE)
	{
	  error_print ("Too many storage (%d)", storage);
	  storage = ELEKTRON_MAX_STORAGE;
	}
      for (guint j = 0; j < storage; j++)
	{
	  json_reader_read_element (reader, j);
	  const gchar *storage_name = json_reader_get_string_value (reader);
	  const gchar **stname = FS_TYPE_NAMES;
	  guint id = 1;
	  while (*stname)
	    {
	      if (strcmp (*stname, storage_name) == 0)
		{
		  data->dev_desc.storage |= id;
		  break;
		}
	      stname++;
	      id <<= 1;
	    }
	  json_reader_end_element (reader);
	}
      json_reader_end_element (reader);

      break;
    }

cleanup_reader:
  g_object_unref (reader);
cleanup_parser:
  g_object_unref (parser);
  if (err)
    {
      data->dev_desc.id = -1;
    }
  return err;
}

static gint
elektron_configure_device (struct backend *backend, guint8 id)
{

  gchar *filename = get_user_dir (CONF_DIR DEVICES_FILE);
  gint err = elektron_configure_device_from_file (backend, id, filename);
  g_free (filename);

  if (err)
    {
      filename = g_build_filename (get_data_dir (), DEVICES_FILE, NULL);
      err = elektron_configure_device_from_file (backend, id, filename);
      g_free (filename);
    }

  return err;
}

GByteArray *
elektron_ping (struct backend *backend)
{
  GByteArray *tx_msg, *rx_msg;
  struct elektron_data *data = g_malloc (sizeof (struct elektron_data));

  data->seq = 0;
  backend->data = data;

  tx_msg = elektron_new_msg (PING_REQUEST, sizeof (PING_REQUEST));
  rx_msg = elektron_tx_and_rx_timeout (backend, tx_msg,
				       BE_SYSEX_TIMEOUT_GUESS_MS, NULL);
  if (!rx_msg)
    {
      backend->data = NULL;
      g_free (data);
    }

  return rx_msg;
}

void
elektron_destroy_data (struct backend *backend)
{
  struct elektron_data *data = backend->data;
  struct elektron_dev_desc *dev_desc = &data->dev_desc;
  struct elektron_fs_desc *fs_desc = dev_desc->fs_descs;

  debug_print (1, "Destroying backend elektron data...");


  for (guint i = 0; i < dev_desc->fs_descs_len; i++)
    {
      gchar **ext = fs_desc->extensions;
      for (guint j = 0; j < ELEKTRON_MAX_EXTENSIONS; j++)
	{
	  if (!*ext)
	    {
	      break;
	    }

	  g_free (*ext);
	  ext++;
	}
      fs_desc++;
    }

  g_free (backend->data);
  backend->data = NULL;
}

static gint
elektron_handshake (struct backend *backend)
{
  guint8 id;
  gchar *overbridge_name;
  GByteArray *tx_msg, *rx_msg;
  struct elektron_data *data;

  rx_msg = elektron_ping (backend);
  if (!rx_msg)
    {
      return -ENODEV;
    }

  data = backend->data;

  overbridge_name = strdup ((gchar *) & rx_msg->data[7 + rx_msg->data[6]]);
  id = rx_msg->data[5];
  free_msg (rx_msg);

  if (elektron_configure_device (backend, id))
    {
      backend->data = NULL;
      g_free (overbridge_name);
      g_free (data);
      return -ENODEV;
    }

  g_usleep (BE_REST_TIME_US);

  tx_msg = elektron_new_msg (SOFTWARE_VERSION_REQUEST,
			     sizeof (SOFTWARE_VERSION_REQUEST));
  rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
  if (!rx_msg)
    {
      backend->data = NULL;
      g_free (overbridge_name);
      g_free (data);
      return -ENODEV;
    }
  snprintf (backend->version, LABEL_MAX, "%s", (gchar *) & rx_msg->data[10]);
  free_msg (rx_msg);

  g_usleep (BE_REST_TIME_US);

  if (debug_level > 1)
    {
      tx_msg = elektron_new_msg (DEVICEUID_REQUEST,
				 sizeof (DEVICEUID_REQUEST));
      rx_msg = elektron_tx_and_rx (backend, tx_msg, NULL);
      if (rx_msg)
	{
	  debug_print (1, "UID: %x", *((guint32 *) & rx_msg->data[5]));
	  free_msg (rx_msg);
	}

      g_usleep (BE_REST_TIME_US);
    }

  snprintf (backend->description, LABEL_MAX, "%s", overbridge_name);

  g_free (overbridge_name);

  backend->destroy_data = elektron_destroy_data;
  backend->upgrade_os = elektron_upgrade_os;
  backend->get_storage_stats =
    data->dev_desc.storage ? elektron_get_storage_stats : NULL;

  return 0;
}

static gint
elektron_download_data_snd_pkg (struct backend *backend,
				const gchar *path, struct idata *pkg,
				struct task_control *control)
{
  return elektron_download_pkg (backend, path, pkg, control,
				PKG_FILE_TYPE_DATA_SOUND,
				&FS_DATA_SND_OPERATIONS,
				elektron_download_data_snd);
}

static gint
elektron_download_data_prj_pkg (struct backend *backend,
				const gchar *path, struct idata *pkg,
				struct task_control *control)
{
  return elektron_download_pkg (backend, path, pkg, control,
				PKG_FILE_TYPE_DATA_PROJECT,
				&FS_DATA_PRJ_OPERATIONS,
				elektron_download_data_prj);
}

static gint
elektron_download_data_pst_pkg (struct backend *backend,
				const gchar *path, struct idata *pkg,
				struct task_control *control)
{
  return elektron_download_pkg (backend, path, pkg, control,
				PKG_FILE_TYPE_DATA_PRESET,
				&FS_DATA_PST_OPERATIONS,
				elektron_download_data_pst);
}

static gint
elektron_download_raw_pst_pkg (struct backend *backend, const gchar *path,
			       struct idata *pkg,
			       struct task_control *control)
{
  return elektron_download_pkg (backend, path, pkg, control,
				PKG_FILE_TYPE_RAW_PRESET,
				&FS_RAW_ANY_OPERATIONS,
				elektron_download_raw);
}

static gint
elektron_upload_data_snd_pkg (struct backend *backend, const gchar *path,
			      struct idata *pkg, struct task_control *control)
{
  return elektron_upload_pkg (backend, path, pkg, control,
			      PKG_FILE_TYPE_DATA_SOUND,
			      &FS_DATA_SND_OPERATIONS,
			      elektron_upload_data_snd);
}

static gint
elektron_upload_data_prj_pkg (struct backend *backend, const gchar *path,
			      struct idata *pkg, struct task_control *control)
{
  return elektron_upload_pkg (backend, path, pkg, control,
			      PKG_FILE_TYPE_DATA_PROJECT,
			      &FS_DATA_PRJ_OPERATIONS,
			      elektron_upload_data_prj);
}

static gint
elektron_upload_data_pst_pkg (struct backend *backend, const gchar *path,
			      struct idata *pkg, struct task_control *control)
{
  return elektron_upload_pkg (backend, path, pkg, control,
			      PKG_FILE_TYPE_DATA_PRESET,
			      &FS_DATA_PST_OPERATIONS,
			      elektron_upload_data_pst);
}

static gint
elektron_upload_raw_pst_pkg (struct backend *backend, const gchar *path,
			     struct idata *pkg, struct task_control *control)
{
  return elektron_upload_pkg (backend, path, pkg, control,
			      PKG_FILE_TYPE_RAW_PRESET,
			      &FS_RAW_ANY_OPERATIONS, elektron_upload_raw);
}

const struct connector CONNECTOR_ELEKTRON = {
  .handshake = elektron_handshake,
  .name = "elektron",
  .type = CONNECTOR_TYPE_MIDI,
  .regex = ".*Elektron.*"
};
