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