13aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
23aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * \File playlist-spl.c
33aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
43aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Playlist_t to Samsung (.spl) and back conversion functions.
53aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
63aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Copyright (C) 2008 Alistair Boyle <alistair.js.boyle@gmail.com>
73aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
83aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * This library is free software; you can redistribute it and/or
93aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * modify it under the terms of the GNU Lesser General Public
103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * License as published by the Free Software Foundation; either
113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * version 2 of the License, or (at your option) any later version.
123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * This library is distributed in the hope that it will be useful,
143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * but WITHOUT ANY WARRANTY; without even the implied warranty of
153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Lesser General Public License for more details.
173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * You should have received a copy of the GNU Lesser General Public
193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * License along with this library; if not, write to the
203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Boston, MA 02111-1307, USA.
223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <config.h>
253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <stdio.h>
273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <stdlib.h> // mkstmp()
283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <unistd.h>
293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <errno.h>
303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <sys/stat.h>
313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <sys/types.h>
323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#ifdef HAVE_SYS_UIO_H
333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <sys/uio.h>
343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#endif
353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <fcntl.h>
363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include <string.h>
383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include "libmtp.h"
403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include "libusb-glue.h"
413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include "ptp.h"
423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include "unicode.h"
433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#include "playlist-spl.h"
453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// set this to 1 to add lots of messy debug output to the playlist code
473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#define DEBUG_ENABLED 0
483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// debug macro
503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// d = indenting depth
513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#define IF_DEBUG() if(DEBUG_ENABLED) {\
523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                     printf("%s:%u:%s(): ", __FILE__, __LINE__, __func__); \
533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                   } \
543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                   if(DEBUG_ENABLED)
553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// Internal singly linked list of strings
573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// used to hold .spl playlist in memory
583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevtypedef struct text_struct {
593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* text; // String
603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  struct text_struct *next; // Link to next line, NULL if end of list
613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev} text_t;
623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Forward declarations of local (static) functions.
663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic text_t* read_into_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd);
683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void write_from_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd, text_t* p);
693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void free_spl_text_t(text_t* p);
703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void print_spl_text_t(text_t* p);
713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t trackno_spl_text_t(text_t* p);
723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void tracks_from_spl_text_t(text_t* p, uint32_t* tracks, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void spl_text_t_from_tracks(text_t** p, uint32_t* tracks, const uint32_t trackno, const uint32_t ver_major, const uint32_t ver_minor, char* dnse, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t discover_id_from_filepath(const char* s, LIBMTP_folder_t* folders, LIBMTP_file_t* files); // TODO add file/dir cached args
763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void discover_filepath_from_id(char** p, uint32_t track, LIBMTP_folder_t* folders, LIBMTP_file_t* files);
773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void find_folder_name(LIBMTP_folder_t* folders, uint32_t* id, char** name);
783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t find_folder_id(LIBMTP_folder_t* folders, uint32_t parent, char* name);
793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void append_text_t(text_t** t, char* s);
813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Decides if the indicated object index is an .spl playlist.
873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param oi object we are deciding on
893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return 1 if this is a Samsung .spl object, 0 otherwise
903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevint is_spl_playlist(PTPObjectInfo *oi)
923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return (oi->ObjectFormat == PTP_OFC_Undefined) &&
943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev         (strlen(oi->Filename) > 4) &&
953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev         (strcmp((oi->Filename + strlen(oi->Filename) -4), ".spl") == 0);
963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#ifndef HAVE_MKSTEMP
993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev# ifdef __WIN32__
1003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#  include <fcntl.h>
1013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#  define mkstemp(_pattern) _open(_mktemp(_pattern), _O_CREAT | _O_SHORT_LIVED | _O_EXCL)
1023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev# else
1033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#  error Missing mkstemp() function.
1043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev# endif
1053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev#endif
1063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
1083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Take an object ID, a .spl playlist on the MTP device,
1093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * and convert it to a playlist_t object.
1103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
1113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param device mtp device pointer
1123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param oi object we are reading
1133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param id .spl playlist id on MTP device
1143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param pl the LIBMTP_playlist_t pointer to be filled with info from id
1153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
1163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevvoid spl_to_playlist_t(LIBMTP_mtpdevice_t* device, PTPObjectInfo *oi,
1183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                       const uint32_t id, LIBMTP_playlist_t * const pl)
1193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
1203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // Fill in playlist metadata
1213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // Use the Filename as the playlist name, dropping the ".spl" extension
1223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->name = malloc(sizeof(char)*(strlen(oi->Filename) -4 +1));
1233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  memcpy(pl->name, oi->Filename, strlen(oi->Filename) -4);
1243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // Set terminating character
1253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->name[strlen(oi->Filename) - 4] = 0;
1263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->playlist_id = id;
1273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->parent_id = oi->ParentObject;
1283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->storage_id = oi->StorageID;
1293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->tracks = NULL;
1303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->no_tracks = 0;
1313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("pl->name='%s'\n",pl->name);
1333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // open a temporary file
1353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char tmpname[] = "/tmp/mtp-spl2pl-XXXXXX";
1363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int fd = mkstemp(tmpname);
1373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(fd < 0) {
1383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("failed to make temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
1393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return;
1403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
1413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // make sure the file will be deleted afterwards
1423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(unlink(tmpname) < 0)
1433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("failed to delete temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
144bf992f9881b17ef13f0877b128001e4d001d7c56Yavor Goulishev  int ret = LIBMTP_Get_File_To_File_Descriptor(device, pl->playlist_id, fd, NULL, NULL, NULL);
1453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if( ret < 0 ) {
1463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // FIXME     add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Playlist: Could not get .spl playlist file.");
1473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    close(fd);
1483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("FIXME closed\n");
1493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
1503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* p = read_into_spl_text_t(device, fd);
1523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  close(fd);
1533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // FIXME cache these somewhere else so we don't keep calling this!
1553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_folder_t *folders;
1563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_file_t *files;
1573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  folders = LIBMTP_Get_Folder_List(device);
1583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  files = LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
1593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // convert the playlist listing to track ids
1613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->no_tracks = trackno_spl_text_t(p);
1623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("%u track%s found\n", pl->no_tracks, pl->no_tracks==1?"":"s");
1633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->tracks = malloc(sizeof(uint32_t)*(pl->no_tracks));
1643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  tracks_from_spl_text_t(p, pl->tracks, folders, files);
1653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  free_spl_text_t(p);
1673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // debug: add a break since this is the top level function call
1693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("------------\n\n");
1703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
1713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
1743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Push a playlist_t onto the device after converting it to a .spl format
1753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
1763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param device mtp device pointer
1773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param pl the LIBMTP_playlist_t to convert (pl->playlist_id will be updated
1783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *           with the newly created object's id)
1793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return 0 on success, any other value means failure.
1803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
1813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevint playlist_t_to_spl(LIBMTP_mtpdevice_t *device,
1823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                      LIBMTP_playlist_t * const pl)
1833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
1843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* t;
1853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_folder_t *folders;
1863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_file_t *files;
1873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  folders = LIBMTP_Get_Folder_List(device);
1883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  files = LIBMTP_Get_Filelisting_With_Callback(device, NULL, NULL);
1893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char tmpname[] = "/tmp/mtp-spl2pl-XXXXXX"; // must be a var since mkstemp modifies it
1913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("pl->name='%s'\n",pl->name);
1933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
1943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // open a file descriptor
1953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int fd = mkstemp(tmpname);
1963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(fd < 0) {
1973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("failed to make temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
1983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return -1;
1993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
2003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // make sure the file will be deleted afterwards
2013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(unlink(tmpname) < 0)
2023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("failed to delete temp file for %s.spl -> %s, errno=%s\n", pl->name, tmpname, strerror(errno));
2033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // decide on which version of the .spl format to use
2053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t ver_major;
2063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t ver_minor = 0;
2073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
2083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(FLAG_PLAYLIST_SPL_V2(ptp_usb)) ver_major = 2;
2093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  else ver_major = 1; // FLAG_PLAYLIST_SPL_V1()
2103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("%u track%s\n", pl->no_tracks, pl->no_tracks==1?"":"s");
2123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf(".spl version %d.%02d\n", ver_major, ver_minor);
2133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // create the text for the playlist
2153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  spl_text_t_from_tracks(&t, pl->tracks, pl->no_tracks, ver_major, ver_minor, NULL, folders, files);
2163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  write_from_spl_text_t(device, fd, t);
2173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  free_spl_text_t(t); // done with the text
2183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // create the file object for storing
2203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_file_t* f = malloc(sizeof(LIBMTP_file_t));
2213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->item_id = 0;
2223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->parent_id = pl->parent_id;
2233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->storage_id = pl->storage_id;
2243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->filename = malloc(sizeof(char)*(strlen(pl->name)+5));
2253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  strcpy(f->filename, pl->name);
2263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  strcat(f->filename, ".spl"); // append suffix
2273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->filesize = lseek(fd, 0, SEEK_CUR); // file desc is currently at end of file
2283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->filetype = LIBMTP_FILETYPE_UNKNOWN;
2293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  f->next = NULL;
2303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("%s is %dB\n", f->filename, (int)f->filesize);
2323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // push the playlist to the device
2343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  lseek(fd, 0, SEEK_SET); // reset file desc. to start of file
2353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, f, NULL, NULL);
2363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  pl->playlist_id = f->item_id;
2373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  free(f->filename);
2383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  free(f);
2393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // release the memory when we're done with it
2413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  close(fd);
2423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // debug: add a break since this is the top level function call
2433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("------------\n\n");
2443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return ret;
2463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
2473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
2513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Update a playlist on the device. If only the playlist's name is being
2523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * changed the pl->playlist_id will likely remain the same. An updated track
2533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * list will result in the old playlist being replaced (ie: new playlist_id).
2543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * NOTE: Other playlist metadata aside from playlist name and tracks are
2553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * ignored.
2563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
2573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param device mtp device pointer
2583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param new the LIBMTP_playlist_t to convert (pl->playlist_id will be updated
2593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *           with the newly created object's id)
2603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return 0 on success, any other value means failure.
2613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
2623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevint update_spl_playlist(LIBMTP_mtpdevice_t *device,
2633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev			  LIBMTP_playlist_t * const newlist)
2643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
2653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("pl->name='%s'\n",newlist->name);
2663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // read in the playlist of interest
2683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_playlist_t * old = LIBMTP_Get_Playlist(device, newlist->playlist_id);
2693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // check to see if we found it
2713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if (!old)
2723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return -1;
2733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // check if the playlists match
2753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int delta = 0;
2763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int i;
2773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(old->no_tracks != newlist->no_tracks)
2783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    delta++;
2793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  for(i=0;i<newlist->no_tracks && delta==0;i++) {
2803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(old->tracks[i] != newlist->tracks[i])
2813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      delta++;
2823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
2833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // if not, kill the playlist and replace it
2853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(delta) {
2863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf("new tracks detected:\n");
2873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf("delete old playlist and build a new one\n");
2883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf(" NOTE: new playlist_id will result!\n");
2893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(LIBMTP_Delete_Object(device, old->playlist_id) != 0)
2903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      return -1;
2913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() {
2933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      if(strcmp(old->name,newlist->name) == 0)
2943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("name unchanged\n");
2953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      else
2963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("name is changing too -> %s\n",newlist->name);
2973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
2983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
2993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return LIBMTP_Create_New_Playlist(device, newlist);
3003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
3013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // update the name only
3043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(strcmp(old->name,newlist->name) != 0) {
3053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf("ONLY name is changing -> %s\n",newlist->name);
3063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf("playlist_id will remain unchanged\n");
3073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    char* s = malloc(sizeof(char)*(strlen(newlist->name)+5));
3083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    strcpy(s, newlist->name);
3093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    strcat(s,".spl"); // FIXME check for success
3103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    int ret = LIBMTP_Set_Playlist_Name(device, newlist, s);
3113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    free(s);
3123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return ret;
3133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
3143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() printf("no change\n");
3163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return 0; // nothing to be done, success
3173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
3183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
3213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Load a file descriptor into a string.
3223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
3233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param device a pointer to the current device.
3243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *               (needed for ucs2->utf8 charset conversion)
3253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param fd the file descriptor to load
3263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return text_t* a linked list of lines of text, id is left blank, NULL if nothing read in
3273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
3283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic text_t* read_into_spl_text_t(LIBMTP_mtpdevice_t *device, const int fd)
3293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
3303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // set MAXREAD to match STRING_BUFFER_LENGTH in unicode.h conversion function
3313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  const size_t MAXREAD = 1024*2;
3323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char t[MAXREAD];
3333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // upto 3 bytes per utf8 character, 2 bytes per ucs2 character,
3343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // +1 for '\0' at end of string
3353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  const size_t WSIZE = MAXREAD/2*3+1;
3363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char w[WSIZE];
3373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* it = t; // iterator on t
3383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* iw = w;
3393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  ssize_t rdcnt;
3403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  off_t offcnt;
3413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* head = NULL;
3423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* tail = NULL;
3433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int eof = 0;
3443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // reset file descriptor (fd) to start of file
3463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  offcnt = lseek(fd, 0, SEEK_SET);
3473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(!eof) {
3493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // find the current offset in the file
3503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // to allow us to determine how many bytes we read if we hit the EOF
3513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // where returned rdcnt=0 from read()
3523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    offcnt = lseek(fd, 0, SEEK_CUR);
3533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // read to refill buffer
3543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // (there might be data left from an incomplete last string in t,
3553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // hence start filling at it)
3563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    it = t; // set ptr to start of buffer
3573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    rdcnt = read(fd, it, sizeof(char)*MAXREAD);
3583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(rdcnt < 0)
3593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("load_spl_fd read err %s\n", strerror(errno));
3603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else if(rdcnt == 0) { // for EOF, fix rdcnt
3613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      if(it-t == MAXREAD)
3623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("error -- buffer too small to read in .spl playlist entry\n");
3633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      rdcnt = lseek(fd, 0, SEEK_CUR) - offcnt;
3653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      eof = 1;
3663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
3673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() printf("read buff= {%dB new, %dB old/left-over}%s\n",(int)rdcnt, (int)(iw-w), eof?", EOF":"");
3693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // while more input bytes
3713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    char* it_end = t + rdcnt;
3723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    while(it < it_end) {
3733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      // copy byte, unless EOL (then replace with end-of-string \0)
3743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      if(*it == '\r' || *it == '\n')
3753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        *iw = '\0';
3763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      else
3773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        *iw = *it;
3783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      it++;
3803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      iw++;
3813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      // EOL -- store it
3833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      if( (iw-w) >= 2 && // we must have at least two bytes
3843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          *(iw-1) == '\0' && *(iw-2) == '\0' && // 0x0000 is end-of-string
3853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          // but it must be aligned such that we have an {odd,even} set of
3863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          // bytes since we are expecting to consume bytes two-at-a-time
3873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          !((iw-w)%2) ) {
3883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        // drop empty lines
3903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        //  ... cast as a string of 2 byte characters
3913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        if(ucs2_strlen((uint16_t*)w) == 0) {
3923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          iw = w;
3933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          continue;
3943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        }
3953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
3963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        // create a new node in the list
3973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        if(head == NULL) {
3983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          head = malloc(sizeof(text_t));
3993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          tail = head;
4003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        }
4013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        else {
4023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          tail->next = malloc(sizeof(text_t));
4033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          tail = tail->next;
4043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        }
4053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        // fill in the data for the node
4063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        //  ... cast as a string of 2 byte characters
4073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        tail->text = utf16_to_utf8(device, (uint16_t*) w);
4083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        iw = w; // start again
4093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        IF_DEBUG() printf("line: %s\n", tail->text);
4113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      }
4123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      // prevent buffer overflow
4143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      if(iw >= w + WSIZE) {
4153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        // if we ever see this error its BAD:
4163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        //   we are dropping all the processed bytes for this line and
4173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        //   proceeding on as if everything is okay, probably losing a track
4183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        //   from the playlist
4193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("ERROR %s:%u:%s(): buffer overflow! .spl line too long @ %zuB\n",
4203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev               __FILE__, __LINE__, __func__, WSIZE);
4213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        iw = w; // reset buffer
4223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      }
4233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
4243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // if the last thing we did was save our line, then we finished working
4263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // on the input buffer and we can start fresh
4273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // otherwise we need to save our partial work, if we're not quiting (eof).
4283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // there is nothing special we need to do, to achieve this since the
4293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // partially completed string will sit in 'w' until we return to complete
4303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // the line
4313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
4333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // set the next pointer at the end
4353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // if there is any list
4363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(head != NULL)
4373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    tail->next = NULL;
4383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // return the head of the list (NULL if no list)
4403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return head;
4413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
4423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
4453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Write a .spl text file to a file in preparation for pushing it
4463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * to the device.
4473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
4483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param fd file descriptor to write to
4493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the text to output one line per string in the linked list
4503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see playlist_t_to_spl()
4513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
4523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void write_from_spl_text_t(LIBMTP_mtpdevice_t *device,
4533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                  const int fd,
4543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                  text_t* p) {
4553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  ssize_t ret;
4563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // write out BOM for utf16/ucs2 (byte order mark)
4573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  ret = write(fd,"\xff\xfe",2);
4583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(p != NULL) {
4593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    char *const t = (char*) utf8_to_utf16(device, p->text);
4603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // note: 2 bytes per ucs2 character
4613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    const size_t len = ucs2_strlen((uint16_t*)t)*sizeof(uint16_t);
4623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    int i;
4633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    IF_DEBUG() {
4653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("\nutf8=%s ",p->text);
4663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      for(i=0;i<strlen(p->text);i++)
4673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("%02x ", p->text[i] & 0xff);
4683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("\n");
4693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("ucs2=");
4703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      for(i=0;i<ucs2_strlen((uint16_t*)t)*sizeof(uint16_t);i++)
4713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("%02x ", t[i] & 0xff);
4723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("\n");
4733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
4743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // write: utf8 -> utf16
4763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    ret += write(fd, t, len);
4773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // release the converted string
4793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    free(t);
4803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // check for failures
4823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(ret < 0)
4833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("write spl file failed: %s\n", strerror(errno));
4843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else if(ret != len +2)
4853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("write spl file wrong number of bytes ret=%d len=%d '%s'\n", (int)ret, (int)len, p->text);
4863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // write carriage return, line feed in ucs2
4883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    ret = write(fd, "\r\0\n\0", 4);
4893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(ret < 0)
4903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("write spl file failed: %s\n", strerror(errno));
4913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else if(ret != 4)
4923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("failed to write the correct number of bytes '\\n'!\n");
4933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // fake out count (first time through has two extra bytes from BOM)
4953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    ret = 2;
4963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
4973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // advance to the next line
4983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    p = p->next;
4993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
5003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
5013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
5033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Destroy a linked-list of strings.
5043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
5053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the list to destroy
5063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see spl_to_playlist_t()
5073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see playlist_t_to_spl()
5083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
5093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void free_spl_text_t(text_t* p)
5103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
5113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* d;
5123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(p != NULL) {
5133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    d = p;
5143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    free(p->text);
5153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    p = p->next;
5163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    free(d);
5173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
5183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
5193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
5213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Print a linked-list of strings to stdout.
5223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
5233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the list to print
5243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
5253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void print_spl_text_t(text_t* p)
5263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
5273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(p != NULL) {
5283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf("%s\n",p->text);
5293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    p = p->next;
5303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
5313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
5323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
5343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Count the number of tracks in this playlist. A track will be counted as
5353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * such if the line starts with a leading slash.
5363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
5373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the text to search
5383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return number of tracks in the playlist
5393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see spl_to_playlist_t()
5403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
5413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t trackno_spl_text_t(text_t* p) {
5423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t c = 0;
5433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(p != NULL) {
5443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(p->text[0] == '\\' ) c++;
5453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    p = p->next;
5463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
5473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return c;
5493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
5503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
5523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the track ids for this playlist's files.
5533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * (ie: \Music\song.mp3 -> 12345)
5543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
5553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the text to search
5563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param tracks returned list of track id's for the playlist_t, must be large
5573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *               enough to accomodate all the tracks as reported by
5583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *               trackno_spl_text_t()
5593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
5603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param fiels the files list for the device
5613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see spl_to_playlist_t()
5623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
5633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void tracks_from_spl_text_t(text_t* p,
5643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   uint32_t* tracks,
5653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   LIBMTP_folder_t* folders,
5663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   LIBMTP_file_t* files)
5673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
5683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t c = 0;
5693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(p != NULL) {
5703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(p->text[0] == '\\' ) {
5713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      tracks[c] = discover_id_from_filepath(p->text, folders, files);
5723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      IF_DEBUG()
5733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("track %d = %s (%u)\n", c+1, p->text, tracks[c]);
5743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      c++;
5753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
5763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    p = p->next;
5773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
5783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
5793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
5813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
5823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the track names (including path) for this playlist's track ids.
5833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * (ie: 12345 -> \Music\song.mp3)
5843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
5853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p the text to search
5863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param tracks list of track id's to look up
5873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
5883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param fiels the files list for the device
5893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see playlist_t_to_spl()
5903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
5913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void spl_text_t_from_tracks(text_t** p,
5923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   uint32_t* tracks,
5933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   const uint32_t trackno,
5943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   const uint32_t ver_major,
5953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   const uint32_t ver_minor,
5963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   char* dnse,
5973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   LIBMTP_folder_t* folders,
5983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                   LIBMTP_file_t* files)
5993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
6003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // HEADER
6023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  text_t* c = NULL;
6033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  append_text_t(&c, "SPL PLAYLIST");
6043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  *p = c; // save the top of the list!
6053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char vs[14]; // "VERSION 2.00\0"
6073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  sprintf(vs,"VERSION %d.%02d",ver_major,ver_minor);
6083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  append_text_t(&c, vs);
6103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  append_text_t(&c, "");
6113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // TRACKS
6133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int i;
6143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* f;
6153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  for(i=0;i<trackno;i++) {
6163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    discover_filepath_from_id(&f, tracks[i], folders, files);
6173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(f != NULL) {
6193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      append_text_t(&c, f);
6203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      IF_DEBUG()
6213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        printf("track %d = %s (%u)\n", i+1, f, tracks[i]);
6223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
6233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else
6243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      printf("failed to find filepath for track=%d\n", tracks[i]);
6253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
6263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // FOOTER
6283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  append_text_t(&c, "");
6293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  append_text_t(&c, "END PLAYLIST");
6303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(ver_major == 2) {
6313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    append_text_t(&c, "");
6323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    append_text_t(&c, "myDNSe DATA");
6333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(dnse != NULL) {
6343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      append_text_t(&c, dnse);
6353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
6363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else {
6373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      append_text_t(&c, "");
6383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      append_text_t(&c, "");
6393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
6403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    append_text_t(&c, "END myDNSe");
6413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
6423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  c->next = NULL;
6443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // debug
6463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  IF_DEBUG() {
6473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    printf(".spl playlist:\n");
6483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    print_spl_text_t(*p);
6493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
6503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
6513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
6543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the track names (including path) given a fileid
6553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * (ie: 12345 -> \Music\song.mp3)
6563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
6573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param p returns the file path (ie: \Music\song.mp3),
6583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *          (*p) == NULL if the look up fails
6593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param track track id to look up
6603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
6613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param files the files list for the device
6623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see spl_text_t_from_tracks()
6633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
6643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev// returns p = NULL on failure, else the filepath to the track including track name, allocated as a correct length string
6663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void discover_filepath_from_id(char** p,
6673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                      uint32_t track,
6683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                      LIBMTP_folder_t* folders,
6693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev                                      LIBMTP_file_t* files)
6703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
6713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // fill in a string from the right side since we don't know the root till the end
6723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  const int M = 1024;
6733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char w[M];
6743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* iw = w + M; // iterator on w
6753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // in case of failure return NULL string
6773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  *p = NULL;
6783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // find the right file
6813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(files != NULL && files->item_id != track) {
6823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    files = files->next;
6833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
6843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // if we didn't find a matching file, abort
6853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(files == NULL)
6863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return;
6873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // stuff the filename into our string
6893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // FIXME: check for string overflow before it occurs
6903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  iw = iw - (strlen(files->filename) +1); // leave room for '\0' at the end
6913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  strcpy(iw,files->filename);
6923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
6933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // next follow the directories to the root
6943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // prepending folders to the path as we go
6953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t id = files->parent_id;
6963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* f = NULL;
6973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(id != 0) {
6983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    find_folder_name(folders, &id, &f);
6993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(f == NULL) return; // fail if the next part of the path couldn't be found
7003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    iw = iw - (strlen(f) +1);
7013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // FIXME: check for string overflow before it occurs
7023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    strcpy(iw, f);
7033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    iw[strlen(f)] = '\\';
7043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    free(f);
7053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
7063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // prepend a slash
7083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  iw--;
7093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  iw[0] = '\\';
7103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // now allocate a string of the right length to be returned
7123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  *p = strdup(iw);
7133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
7143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
7173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the track id given a track's name (including path)
7183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * (ie: \Music\song.mp3 -> 12345)
7193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
7203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param s file path to look up (ie: \Music\song.mp3),
7213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *          (*p) == NULL if the look up fails
7223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
7233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param files the files list for the device
7243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return track id, 0 means failure
7253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see tracks_from_spl_text_t()
7263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
7273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t discover_id_from_filepath(const char* s, LIBMTP_folder_t* folders, LIBMTP_file_t* files)
7283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
7293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // abort if this isn't a path
7303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(s[0] != '\\')
7313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return 0;
7323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  int i;
7343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  uint32_t id = 0;
7353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* sc = strdup(s);
7363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  char* sci = sc +1; // iterator
7373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // skip leading slash in path
7383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // convert all \ to \0
7403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  size_t len = strlen(s);
7413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  for(i=0;i<len;i++) {
7423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(sc[i] == '\\') {
7433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      sc[i] = '\0';
7443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
7453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
7463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // now for each part of the string, find the id
7483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  while(sci != sc + len +1) {
7493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // if its the last part of the string, its the filename
7503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(sci + strlen(sci) == sc + len) {
7513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      while(files != NULL) {
7533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        // check parent matches id and name matches sci
7543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        if( (files->parent_id == id) &&
7553aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev            (strcmp(files->filename, sci) == 0) ) { // found it!
7563aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          id = files->item_id;
7573aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev          break;
7583aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        }
7593aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev        files = files->next;
7603aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      }
7613aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
7623aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    else { // otherwise its part of the directory path
7633aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      id = find_folder_id(folders, id, sci);
7643aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    }
7653aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7663aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    // move to next folder/file
7673aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    sci += strlen(sci) +1;
7683aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
7693aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7703aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // release our copied string
7713aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  free(sc);
7723aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7733aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // FIXME check that we actually have a file
7743aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7753aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  return id;
7763aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
7773aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7783aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7793aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7803aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
7813aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the folder name given the folder's id.
7823aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
7833aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
7843aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param id the folder_id to look up, returns the folder's parent folder_id
7853aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param name returns the name of the folder or NULL on failure
7863aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see discover_filepath_from_id()
7873aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
7883aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void find_folder_name(LIBMTP_folder_t* folders, uint32_t* id, char** name)
7893aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
7903aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7913aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // FIXME this function is exactly LIBMTP_Find_Folder
7923aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
7933aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  LIBMTP_folder_t* f = LIBMTP_Find_Folder(folders, *id);
7943aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(f == NULL) {
7953aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    *name = NULL;
7963aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
7973aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  else { // found it!
7983aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    *name = strdup(f->name);
7993aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    *id = f->parent_id;
8003aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
8013aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
8023aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8033aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8043aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
8053aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Find the folder id given the folder's name and parent id.
8063aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
8073aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param folders the folders list for the device
8083aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param parent the folder's parent's id
8093aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param name the name of the folder
8103aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @return the folder_id or 0 on failure
8113aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see discover_filepath_from_id()
8123aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
8133aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic uint32_t find_folder_id(LIBMTP_folder_t* folders, uint32_t parent, char* name) {
8143aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8153aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(folders == NULL)
8163aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return 0;
8173aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8183aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // found it!
8193aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  else if( (folders->parent_id == parent) &&
8203aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev           (strcmp(folders->name, name) == 0) )
8213aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return folders->folder_id;
8223aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8233aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  // no luck so far, search both siblings and children
8243aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  else {
8253aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    uint32_t id = 0;
8263aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8273aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if(folders->sibling != NULL)
8283aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      id = find_folder_id(folders->sibling, parent, name);
8293aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    if( (id == 0) && (folders->child != NULL) )
8303aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev      id = find_folder_id(folders->child, parent, name);
8313aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8323aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    return id;
8333aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
8343aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
8353aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8363aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev
8373aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev/**
8383aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * Append a string to a linked-list of strings.
8393aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev *
8403aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param t the list-of-strings, returns with the added string
8413aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @param s the string to append
8423aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev * @see spl_text_t_from_tracks()
8433aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev */
8443aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishevstatic void append_text_t(text_t** t, char* s)
8453aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev{
8463aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  if(*t == NULL) {
8473aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    *t = malloc(sizeof(text_t));
8483aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
8493aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  else {
8503aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    (*t)->next = malloc(sizeof(text_t));
8513aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev    (*t) = (*t)->next;
8523aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  }
8533aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev  (*t)->text = strdup(s);
8543aa430dc5437a98734b36f996f9b17081a589143Yavor Goulishev}
855