From: <ny...@us...> - 2007-02-27 09:45:40
|
Revision: 397 http://svn.sourceforge.net/pmplib/?rev=397&view=rev Author: nyaochi Date: 2007-02-27 01:45:39 -0800 (Tue, 27 Feb 2007) Log Message: ----------- More for iPod support. The POSIX version has been broken. - Added "settle" operation for iPods (*1) - Added symbolic-link emulation represented by M3U8 playlist (*2) - Changed the structure of easypmp_filename_t for the convenience. - Diaplay the codec name of MPEG4 AAC - Option remapping: -s (--setvar) to -a (--setvar) - Option remapping: -x (--help-variable) to -H (--help-variable) *1 Settlement of media files This operation was introduced for Apple iPod players to bring them into the world of EasyPMP. Apple iPods for Windows are actually UMS/MSC device like other players supported by PMPlib, but has the strong requirement for the names of media files: a filename should be something like "F01\ABCD.m4a". In other words, we cannot name music files arbitrarily unlike other UMS/MSC players. In order to solve this problem, the operation "settle" (-s, --settle) was introduced to move/rename media files to the locations/names that are suitable for Apple iPods. At the same time, EasyPMP leaves symbolic links (*2) pointing to the settled music files from their original locations. Take the following example where a user places his/her music files under \Music directory of an iPod. [unsettled music files] \Music\RazorLight\RazorLight\01_In The Morning.m4a \Music\RazorLight\RazorLight\02_Who Needs Love.m4a \Music\RazorLight\RazorLight\03_Hold On.m4a \Music\RazorLight\RazorLight\04_America.m4a After EasyPMP performs a settle operation, the directory structure will be changed as follows. [music links] \Music\RazorLight\RazorLight\01_In The Morning.m4a.m3u8 (--> F02\JDEW.m4a) \Music\RazorLight\RazorLight\02_Who Needs Love.m4a.m3u8 (--> F05\IEJV.m4a) \Music\RazorLight\RazorLight\03_Hold On.m4a.m3u8 (--> F00\ESAV.m4a) \Music\RazorLight\RazorLight\04_America.m4a.m3u8 (--> F03\AWQM.m4a) [settled music files] \iPod_Control\Music\F02\JDEW.m4a \iPod_Control\Music\F05\IEJV.m4a \iPod_Control\Music\F00\ESAV.m4a \iPod_Control\Music\F03\AWQM.m4a This directory structure is perfectly compatible for the iPod player. In addition, the user can still listen to the music through M3U8 playlists placed in the \Music directory. If the user would like to remove the file "02_Who Needs Love.m4a" that was moved to "F05\IEJV.m4a", he/she can simply delete "02_Who Needs Love.m4a.m3u8" and run EasyPMP with -E (--remove-unref) option; EasyPMP will delete "F05\IEJV.m4a", which is not pointed by any symbolic links in the \Music directory. In this way, the user does not have to find out the location of settled music files at all. If the user would like to restore the original music files from the settled files, he or she may run EasyPMP with -x (--unsettle) option. This will take back to the original state of the directory structure. *2 Symbolic link emulation for FAT32 Symbolic (or hard) link is not supported by FAT32. EasyPMP supports symbolic links emulated by M3U8 playlists. The content of an M3U8 playlist is mere the reference to a settled media file. The name of an M3U8 symbolic link must be "(original_name).m3u8)", e.g., "01_track.m4a.m3u8". Modified Paths: -------------- trunk/pmplib/frontend/easypmp/common/database.c trunk/pmplib/frontend/easypmp/common/easypmp.h trunk/pmplib/frontend/easypmp/common/enumerate.c trunk/pmplib/frontend/easypmp/common/playlist.c trunk/pmplib/frontend/easypmp/cui/device.c trunk/pmplib/frontend/easypmp/cui/easypmp_cui.vcproj trunk/pmplib/frontend/easypmp/cui/main.c trunk/pmplib/frontend/easypmp/cui/option.c trunk/pmplib/include/pmplib/filepath.h trunk/pmplib/include/pmplib/pmp.h trunk/pmplib/lib/filepath/filepath.vcproj trunk/pmplib/lib/filepath/filepath_win32.c trunk/pmplib/lib/pmp_ipod/pmp_ipod.c trunk/pmplib/lib/pmp_ipod/pmp_ipod.vcproj trunk/pmplib/lib/ucs2/ucs2char.c trunk/pmplib/lib/ucs2/ucs2char_win32.c Added Paths: ----------- trunk/pmplib/frontend/easypmp/common/settle.c Modified: trunk/pmplib/frontend/easypmp/common/database.c =================================================================== --- trunk/pmplib/frontend/easypmp/common/database.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/common/database.c 2007-02-27 09:45:39 UTC (rev 397) @@ -165,10 +165,9 @@ int is_skipping = 0; easypmp_filename_t* target = &fl->elements[i]; pmp_music_record_t* record = &records[i]; - ucs2char_t filename[MAX_PATH]; + const ucs2char_t* filename = target->name; uint32_t timestamp = 0; - filepath_combinepath(filename, MAX_PATH, target->pathname, target->filename); timestamp = (uint32_t)filepath_mtime(filename); // Report the progress. Modified: trunk/pmplib/frontend/easypmp/common/easypmp.h =================================================================== --- trunk/pmplib/frontend/easypmp/common/easypmp.h 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/common/easypmp.h 2007-02-27 09:45:39 UTC (rev 397) @@ -38,11 +38,14 @@ enum { EASYPMPP_START = 0x00010000, EASYPMPP_OPEN = 0x00020000, - EASYPMPP_MUSIC_GMI = 0x00030000, - EASYPMPP_MUSIC_UPDATE = 0x00040000, - EASYPMPP_PLAYLIST_CONVERT = 0x02050000, - EASYPMPP_CLOSE = 0x00060000, - EASYPMPP_END = 0x00070000, + EASYPMPP_REMOVE_UNREF = 0x00030000, + EASYPMPP_SETTLE = 0x00040000, + EASYPMPP_UNSETTLE = 0x00050000, + EASYPMPP_MUSIC_GMI = 0x00060000, + EASYPMPP_MUSIC_UPDATE = 0x00070000, + EASYPMPP_PLAYLIST_CONVERT = 0x02080000, + EASYPMPP_CLOSE = 0x00090000, + EASYPMPP_END = 0x000A0000, EASYPMPSP_START = 0x00000001, EASYPMPSP_PROGRESS = 0x00000002, @@ -70,19 +73,22 @@ #define MAKE_PMP_ERROR(code) (((code) << 16) | EASYPMPE_PMPERROR) enum { - MODE_NONE = 0, - MODE_DATABASE = 0x00000001, + MODE_NONE = 0, + MODE_DATABASE = 0x00000001, MODE_DATABASE_UPDATE = 0x00000002, MODE_DATABASE_REPR = 0x00000004, - MODE_PLAYLIST = 0x00000010, + MODE_PLAYLIST = 0x00000010, MODE_PLAYLIST_RECONVERT = 0x00000100, - MODE_PLAYLIST_FIND = 0x00000200, - MODE_PLAYLIST_SKIP = 0x00000400, - MODE_PLAYLIST_JSPL = 0x00000800, - MODE_LIST_DEVICES = 0x00001000, - MODE_HELP = 0x00010000, - MODE_HELP_VARIABLE = 0x00020000, - MODE_VERSION = 0x00040000, + MODE_PLAYLIST_FIND = 0x00000200, + MODE_PLAYLIST_SKIP = 0x00000400, + MODE_PLAYLIST_JSPL = 0x00000800, + MODE_SETTLE = 0x00001000, + MODE_UNSETTLE = 0x00002000, + MODE_REMOVE_UNREF = 0x00004000, + MODE_LIST_DEVICES = 0x00010000, + MODE_HELP = 0x00100000, + MODE_HELP_VARIABLE = 0x00200000, + MODE_VERSION = 0x00400000, }; typedef struct { @@ -100,13 +106,13 @@ ucs2char_t* path_to_pmp_music; ucs2char_t* path_to_pmp_playlist; + ucs2char_t* path_to_unsettled_music; ucs2char_t* path_to_playlist_source; int subdir_playlist_source; } option_t; typedef struct { - ucs2char_t pathname[MAX_PATH]; - ucs2char_t filename[MAX_PATH]; + ucs2char_t name[MAX_PATH]; } easypmp_filename_t; typedef struct { @@ -121,19 +127,30 @@ int phase, int param_int, double param_float, - ucs2char_t* param_str + const ucs2char_t* param_str ); int easypmp_enumerate_music( easypmp_filelist_t* fl, pmp_t* pmp, + const ucs2char_t* relative_path, const option_t* opt, easypmp_enumerate_progress_t proc, void *instance ); int +easypmp_enumerate_music_links( + easypmp_filelist_t* fl, + pmp_t* pmp, + const ucs2char_t* relative_path, + const option_t* opt, + easypmp_enumerate_progress_t proc, + void *instance + ); + +int easypmp_enumerate_playlist( easypmp_filelist_t* fl, pmp_t* pmp, @@ -176,6 +193,16 @@ easypmp_filelist_t* fl ); +int easypmp_settle( + pmp_t* pmp, + easypmp_filelist_t* musics, + easypmp_filelist_t* musics_unsettled, + easypmp_filelist_t* music_links, + const option_t* opt, + easypmp_progress_t progress, + void *instance + ); + /** @} */ #ifdef __cplusplus Modified: trunk/pmplib/frontend/easypmp/common/enumerate.c =================================================================== --- trunk/pmplib/frontend/easypmp/common/enumerate.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/common/enumerate.c 2007-02-27 09:45:39 UTC (rev 397) @@ -77,8 +77,7 @@ // Set path and file. new_filename = &fl->elements[fl->num_elements++]; - ucs2cpy(new_filename->pathname, found_path); - ucs2cpy(new_filename->filename, found_file); + filepath_combinepath(new_filename->name, MAX_PATH, found_path, found_file); } else { // Exit if the filename does not have a supported extension. return 0; @@ -96,6 +95,7 @@ easypmp_enumerate_music( easypmp_filelist_t* fl, pmp_t* pmp, + const ucs2char_t* relative_path, const option_t* opt, easypmp_enumerate_progress_t proc, void *instance @@ -105,7 +105,7 @@ ucs2char_t music_path[MAX_PATH]; // Decode the music path prefix for system path separators - filepath_combinepath(music_path, MAX_PATH, pmp->info.path_to_root, pmp->info.path_to_music); + filepath_combinepath(music_path, MAX_PATH, pmp->info.path_to_root, relative_path); filepath_addslash(music_path); filepath_decode(music_path); @@ -127,6 +127,96 @@ ); } +static int found_music_link_file(void *instance, const ucs2char_t* found_path, const ucs2char_t* found_file) +{ + uint32_t i; + enumerate_dat_t* ed = (enumerate_dat_t*)instance; + pmp_t* pmp = ed->pmp; + pmp_music_t* pmp_music = ed->pmp->music; + easypmp_filelist_t* fl = ed->fl; + ucs2char_t *filename = alloca(sizeof(ucs2char_t) * (ucs2len(found_file)+1)); + + if (!filepath_is_link(found_file)) { + // Not a link file. + return 0; + } + + // Remove the extension of link. + ucs2cpy(filename, found_file); + filepath_remove_extension(filename); + + // Check if the file has an extension supported by the target player. + for (i = 0;i < pmp->info.num_audio_extensions;++i) { + if (filepath_hasext(filename, pmp->info.audio_extensions[i])) { + break; + } + } + + if (i != pmp->info.num_audio_extensions) { + // Supported music file. + easypmp_filename_t* new_filename = NULL; + + // Expand the target array. + fl->elements = (easypmp_filename_t*)realloc( + fl->elements, + sizeof(easypmp_filename_t) * (fl->num_elements+1) + ); + if (!fl->elements) { + return -1; + } + + // Set path and file. + new_filename = &fl->elements[fl->num_elements++]; + filepath_combinepath(new_filename->name, MAX_PATH, found_path, filename); + } else { + // Exit if the filename does not have a supported extension. + return 0; + } + + // Report the progress. + if (ed->proc) { + ed->proc(ed->instance, found_path, found_file, fl->num_elements); + } + + return 0; +} + +int +easypmp_enumerate_music_links( + easypmp_filelist_t* fl, + pmp_t* pmp, + const ucs2char_t* relative_path, + const option_t* opt, + easypmp_enumerate_progress_t proc, + void *instance + ) +{ + enumerate_dat_t ed; + ucs2char_t music_path[MAX_PATH]; + + // Decode the music path prefix for system path separators + filepath_combinepath(music_path, MAX_PATH, pmp->info.path_to_root, relative_path); + filepath_addslash(music_path); + filepath_decode(music_path); + + fl->num_elements = 0; + fl->elements = NULL; + + memset(&ed, 0, sizeof(ed)); + ed.opt = opt; + ed.fl = fl; + ed.pmp = pmp; + ed.proc = proc; + ed.instance = instance; + + return find_file( + music_path, + 1, + found_music_link_file, + &ed + ); +} + static int found_playlist_file(void *instance, const ucs2char_t* found_path, const ucs2char_t* found_file) { enumerate_dat_t* ed = (enumerate_dat_t*)instance; @@ -149,8 +239,7 @@ // Set path and file. new_filename = &fl->elements[fl->num_elements++]; - ucs2cpy(new_filename->pathname, found_path); - ucs2cpy(new_filename->filename, found_file); + filepath_combinepath(new_filename->name, MAX_PATH, found_path, found_file); } else { // Exit if the filename does not have a supported extension. return 0; Modified: trunk/pmplib/frontend/easypmp/common/playlist.c =================================================================== --- trunk/pmplib/frontend/easypmp/common/playlist.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/common/playlist.c 2007-02-27 09:45:39 UTC (rev 397) @@ -92,8 +92,9 @@ goto error_exit; } for (i = 0;i < musics->num_elements;++i) { - ucs2cpy(mediafiles[i].path, musics->elements[i].pathname); - ucs2cpy(mediafiles[i].file, musics->elements[i].filename); + ucs2cpy(mediafiles[i].path, musics->elements[i].name); + filepath_remove_filespec(mediafiles[i].path); + ucs2cpy(mediafiles[i].file, filepath_skippath(musics->elements[i].name)); } // Prepare playlist conversion for finding music files. @@ -115,9 +116,10 @@ int n = 0; // Generate the source filename - ucs2cpy(plf_path, plf->pathname); + ucs2cpy(plf_path, plf->name); + filepath_remove_filespec(plf_path); filepath_decode(plf_path); - filepath_combinepath(src, MAX_PATH, plf_path, plf->filename); + filepath_combinepath(src, MAX_PATH, plf_path, filepath_skippath(plf->name)); // Report the source filename. if (progress(instance, EASYPMPP_PLAYLIST_CONVERT | EASYPMPSP_PROGRESS, i, 0, src) != 0) { Added: trunk/pmplib/frontend/easypmp/common/settle.c =================================================================== --- trunk/pmplib/frontend/easypmp/common/settle.c (rev 0) +++ trunk/pmplib/frontend/easypmp/common/settle.c 2007-02-27 09:45:39 UTC (rev 397) @@ -0,0 +1,249 @@ +/* + * EasyPMP settlement operation (currently for iPod). + * + * Copyright (c) 2005-2007 Naoaki Okazaki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit + * http://www.gnu.org/copyleft/gpl.html . + * + */ + +/* $Id:$ */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif/*HAVE_CONFIG_H*/ +#ifdef HAVE_STRING_H +#include <string.h> +#endif/*HAVE_STRING_H*/ + +#include <os.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <pmplib/ucs2char.h> +#include <pmplib/filepath.h> +#include <pmplib/pmp.h> +#include <gmi.h> +#include <easypmp.h> + +static void generate_filename(ucs2char_t* filename) +{ + ucs2char_t* p = filename; + *p++ = 'F'; + *p++ = '0'; + *p++ = '0' + (rand() % 10); + *p++ = PATHCHAR; + *p++ = 'A' + (rand() % 26); + *p++ = 'A' + (rand() % 26); + *p++ = 'A' + (rand() % 26); + *p++ = 'A' + (rand() % 26); + *p++ = 0; +} + +static int remove_file_from_list(easypmp_filelist_t* list, int i) +{ + memmove(&list->elements[i], &list->elements[i+1], sizeof(easypmp_filename_t) * (list->num_elements - 1 - i)); + list->elements = (easypmp_filename_t*)realloc( + list->elements, + sizeof(easypmp_filename_t) * (--list->num_elements) + ); + return list->elements == NULL ? -1 : 0; +} + +static int add_file_to_list(easypmp_filelist_t* list, const ucs2char_t* filename) +{ + list->elements = (easypmp_filename_t*)realloc( + list->elements, + sizeof(easypmp_filename_t) * (list->num_elements+1) + ); + if (!list->elements) { + return -1; + } + ucs2cpy(list->elements[list->num_elements++].name, filename); + return 0; +} + +int easypmp_settle( + pmp_t* pmp, + easypmp_filelist_t* musics, + easypmp_filelist_t* musics_unsettled, + easypmp_filelist_t* music_links, + const option_t* opt, + easypmp_progress_t progress, + void *instance + ) +{ + int result = 0; + uint32_t i, j, n; + ucs2char_t link[MAX_PATH]; + ucs2char_t target[MAX_PATH]; + ucs2char_t music[MAX_PATH]; + ucs2char_t directory[MAX_PATH]; + + srand(time(NULL)); + + // Remove music files that are not pointed by any links. + if (opt->verb & MODE_REMOVE_UNREF) { + if (progress(instance, EASYPMPP_REMOVE_UNREF|EASYPMPSP_START, 0, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + + n = 0; + for (i = 0;i < musics->num_elements;++i) { + ucs2cpy(music, musics->elements[i].name); + + // Check the existence of a link referring to the music file. + for (j = 0;j < music_links->num_elements;++j) { + // Obtain the filename pointed by this link. + ucs2cpy(link, music_links->elements[j].name); + if (filepath_followlink(link, target) == 0) { + if (ucs2icmp(music, target) == 0) { + break; + } + } + } + + // Remove the music file if no link is referring to the file. + if (j == music_links->num_elements) { + filepath_removefile(music); + remove_file_from_list(musics, i); + --i; + + if (progress(instance, EASYPMPP_REMOVE_UNREF|EASYPMPSP_PROGRESS, n, 0, music) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + n++; + } + } + + for (i = 0;i < music_links->num_elements;++i) { + ucs2cpy(link, music_links->elements[j].name); + if (filepath_followlink(link, target) != 0) { + filepath_removelinkfile(link); + remove_file_from_list(music_links, i); + --i; + + if (progress(instance, EASYPMPP_REMOVE_UNREF|EASYPMPSP_PROGRESS, n, 0, music) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + n++; + } + } + + if (progress(instance, EASYPMPP_REMOVE_UNREF|EASYPMPSP_END, n, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + } + + // Settle media files to the internal location suitable for the player. + if (opt->verb & MODE_SETTLE) { + if (progress(instance, EASYPMPP_SETTLE|EASYPMPSP_START, 0, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + + n = 0; + for (i = 0;i < musics_unsettled->num_elements;++i) { + // Source music file. + ucs2cpy(music, musics_unsettled->elements[i].name); + + // Destination music file. + do { + filepath_combinepath(target, MAX_PATH, pmp->info.path_to_root, pmp->info.path_to_music); + filepath_addslash(target); + generate_filename(target + ucs2len(target)); + filepath_add_extension(target, filepath_extract_extension(music)); + } while (filepath_file_exists(target)); + + // Create the directories for the destination music file. + ucs2cpy(directory, target); + filepath_remove_filespec(directory); + filepath_createdirs(directory); + + // Move the source file to the destination. + filepath_movefile(music, target); + add_file_to_list(musics, target); + remove_file_from_list(musics_unsettled, i); + --i; + + // Create a symbolic link (or shortcut) from the source to the destination. + filepath_linkfile(target, music); + add_file_to_list(music_links, music); + + if (progress(instance, EASYPMPP_SETTLE|EASYPMPSP_PROGRESS, n, 0, music) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + n++; + } + + if (progress(instance, EASYPMPP_SETTLE|EASYPMPSP_END, n, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + } + + if (opt->verb & MODE_UNSETTLE) { + if (progress(instance, EASYPMPP_UNSETTLE|EASYPMPSP_START, 0, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + + n = 0; + for (i = 0;i < music_links->num_elements;++i) { + // Source music link. + ucs2cpy(link, music_links->elements[i].name); + + // Follow the link. + if (filepath_followlink(link, target) == 0) { + // Restore the music file in the original location. + filepath_movefile(target, link); + add_file_to_list(musics_unsettled, link); + + // Remove the settled music file from the music list. + for (j = 0;j < musics->num_elements;++j) { + if (ucs2cmp(musics->elements[j].name, link) == 0) { + remove_file_from_list(musics, j); + break; + } + } + + // Remove the link file. + filepath_removelinkfile(link); + remove_file_from_list(music_links, i); + --i; + + if (progress(instance, EASYPMPP_UNSETTLE|EASYPMPSP_PROGRESS, n, 0, music) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + n++; + } + } + + if (progress(instance, EASYPMPP_UNSETTLE|EASYPMPSP_END, n, 0, NULL) != 0) { + result = EASYPMPE_CANCEL; + goto error_exit; + } + } + +error_exit: + return result; +} Property changes on: trunk/pmplib/frontend/easypmp/common/settle.c ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Modified: trunk/pmplib/frontend/easypmp/cui/device.c =================================================================== --- trunk/pmplib/frontend/easypmp/cui/device.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/cui/device.c 2007-02-27 09:45:39 UTC (rev 397) @@ -35,11 +35,6 @@ #include "util.h" -/** - * \addtogroup cui - * @{ - */ - // Display a path name using the given string format. void device_show_path(FILE *fp, const char* const format, const ucs2char_t* const path) { @@ -65,10 +60,11 @@ fprintf(fp, " Audio codec(s): "); for (i = 0;i < pmp->info.num_audio_codecs;++i) { switch (pmp->info.audio_codecs[i]) { - case PMPCODEC_MPEGLAYER3: fputs("MP3", fp); break; - case PMPCODEC_WMA: fputs("WMA", fp); break; + case PMPCODEC_MPEGLAYER3: fputs("MP3", fp); break; + case PMPCODEC_MPEG4AUDIO: fputs("AAC (MP4)", fp); break; + case PMPCODEC_WMA: fputs("WMA", fp); break; case PMPCODEC_VORBIS: fputs("Ogg Vorbis", fp); break; - case PMPCODEC_WAV: fputs("WAV", fp); break; + case PMPCODEC_WAV: fputs("WAV", fp); break; } if (i != pmp->info.num_audio_codecs-1) fputs(", ", fp); } @@ -102,4 +98,3 @@ { pmplib_enumerate_devid(pmplib, enumerate_devid_callback, pmplib); } -/** @} */ Modified: trunk/pmplib/frontend/easypmp/cui/easypmp_cui.vcproj =================================================================== --- trunk/pmplib/frontend/easypmp/cui/easypmp_cui.vcproj 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/cui/easypmp_cui.vcproj 2007-02-27 09:45:39 UTC (rev 397) @@ -4,6 +4,7 @@ Version="8.00" Name="easypmp_cui" ProjectGUID="{FA1F30D4-6100-4379-8506-6CFE023B0AE7}" + RootNamespace="easypmp_cui" Keyword="Win32Proj" > <Platforms> @@ -255,6 +256,10 @@ RelativePath="..\common\playlist.c" > </File> + <File + RelativePath="..\common\settle.c" + > + </File> </Filter> </Files> <Globals> Modified: trunk/pmplib/frontend/easypmp/cui/main.c =================================================================== --- trunk/pmplib/frontend/easypmp/cui/main.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/cui/main.c 2007-02-27 09:45:39 UTC (rev 397) @@ -84,7 +84,7 @@ int phase, int param_int, double param_float, - ucs2char_t* param_str + const ucs2char_t* param_str ) { FILE *fpo = stdout; @@ -108,6 +108,42 @@ case EASYPMPP_END: break; + case EASYPMPP_REMOVE_UNREF|EASYPMPSP_START: + fprintf(fpo, "Removing media files if they are pointed from no links\n"); + break; + case EASYPMPP_REMOVE_UNREF|EASYPMPSP_PROGRESS: + easypmp_progress_num_str(fpo, param_int+1, filepath_skippath(param_str)); + break; + case EASYPMPP_REMOVE_UNREF|EASYPMPSP_END: + console_clearln(fpo); + fprintf(fpo, " %d files were removed\n", param_int); + fprintf(fpo, "\n"); + break; + + case EASYPMPP_SETTLE|EASYPMPSP_START: + fprintf(fpo, "Settling media files to the internal location suitable for the player\n"); + break; + case EASYPMPP_SETTLE|EASYPMPSP_PROGRESS: + easypmp_progress_num_str(fpo, param_int+1, filepath_skippath(param_str)); + break; + case EASYPMPP_SETTLE|EASYPMPSP_END: + console_clearln(fpo); + fprintf(fpo, " %d files were settled\n", param_int); + fprintf(fpo, "\n"); + break; + + case EASYPMPP_UNSETTLE|EASYPMPSP_START: + fprintf(fpo, "Restoring media files from the internal location of the player\n"); + break; + case EASYPMPP_UNSETTLE|EASYPMPSP_PROGRESS: + easypmp_progress_num_str(fpo, param_int+1, filepath_skippath(param_str)); + break; + case EASYPMPP_UNSETTLE|EASYPMPSP_END: + console_clearln(fpo); + fprintf(fpo, " %d files were restored\n", param_int); + fprintf(fpo, "\n"); + break; + case EASYPMPP_MUSIC_GMI|EASYPMPSP_START: fprintf(fpo, "Obtaining media information from %d files\n", param_int); break; @@ -196,6 +232,7 @@ pmp_t* pmp = NULL; pmplib_t* pmplib = NULL; easypmp_filelist_t musics, playlists; + easypmp_filelist_t musics_unsettled, music_links; pmp_music_record_t* records = NULL; uint32_t num_records = 0; uint32_t openflag = 0; @@ -369,17 +406,43 @@ // Enumerate music files for database and playlist modes. memset(&musics, 0, sizeof(musics)); - if ((opt.verb & MODE_DATABASE) || (opt.verb & MODE_PLAYLIST)) { + if (opt.verb & (MODE_DATABASE|MODE_PLAYLIST|MODE_SETTLE|MODE_UNSETTLE|MODE_REMOVE_UNREF)) { fprintf(fpo, "Enumerating music files in "); fputucs2str_path(fpo, pmp->info.path_to_root); fputucs2str_path(fpo, pmp->info.path_to_music); fprintf(fpo, "\n"); - easypmp_enumerate_music(&musics, pmp, &opt, easypmp_enumerate_progress, NULL); + easypmp_enumerate_music(&musics, pmp, pmp->info.path_to_music, &opt, easypmp_enumerate_progress, NULL); console_clearln(fpo); fprintf(fpo, " %d music files were found\n", musics.num_elements); fprintf(fpo, "\n"); } + // Enumerate unsettled music files. + memset(&musics_unsettled, 0, sizeof(musics_unsettled)); + if (opt.verb & (MODE_SETTLE|MODE_UNSETTLE|MODE_REMOVE_UNREF)) { + fprintf(fpo, "Enumerating unsettled music files in "); + fputucs2str_path(fpo, pmp->info.path_to_root); + fputucs2str_path(fpo, opt.path_to_unsettled_music); + fprintf(fpo, "\n"); + easypmp_enumerate_music(&musics_unsettled, pmp, opt.path_to_unsettled_music, &opt, easypmp_enumerate_progress, NULL); + console_clearln(fpo); + fprintf(fpo, " %d music files were found\n", musics_unsettled.num_elements); + fprintf(fpo, "\n"); + } + + // Enumerate music links. + memset(&music_links, 0, sizeof(music_links)); + if (opt.verb & (MODE_SETTLE|MODE_UNSETTLE|MODE_REMOVE_UNREF)) { + fprintf(fpo, "Enumerating music links in "); + fputucs2str_path(fpo, pmp->info.path_to_root); + fputucs2str_path(fpo, opt.path_to_unsettled_music); + fprintf(fpo, "\n"); + easypmp_enumerate_music_links(&music_links, pmp, opt.path_to_unsettled_music, &opt, easypmp_enumerate_progress, NULL); + console_clearln(fpo); + fprintf(fpo, " %d music links were found\n", music_links.num_elements); + fprintf(fpo, "\n"); + } + // Enumerate playlist files for playlist mode. memset(&playlists, 0, sizeof(playlists)); if (opt.verb & MODE_PLAYLIST) { @@ -397,14 +460,24 @@ fprintf(fpo, "\n"); } + // Settle/unsettle media files (mostly for iPods). + if (opt.verb & (MODE_SETTLE|MODE_UNSETTLE|MODE_REMOVE_UNREF)) { + easypmp_settle(pmp, &musics, &musics_unsettled, &music_links, &opt, easypmp_progress, NULL); + } + // Update database entries for database mode and JSPL playlists. - if ((opt.verb & MODE_DATABASE) || (opt.verb & MODE_PLAYLIST_JSPL)) { + if (opt.verb & (MODE_DATABASE|MODE_PLAYLIST_JSPL)) { easypmp_database(pmp, &musics, &opt, &records, &num_records, easypmp_progress, NULL); } // Convert playlists. if (opt.verb & MODE_PLAYLIST) { - easypmp_playlist(pmp, &playlists, &musics, &opt, records, num_records, easypmp_progress, NULL); + if (opt.verb & MODE_SETTLE) { + // Playlist referring to music_links. + easypmp_playlist(pmp, &playlists, &music_links, &opt, records, num_records, easypmp_progress, NULL); + } else { + easypmp_playlist(pmp, &playlists, &musics, &opt, records, num_records, easypmp_progress, NULL); + } } // Dump the database if specified. @@ -450,5 +523,9 @@ pmplib = NULL; } option_finish(&opt); +#ifdef _WIN32 + CoUninitialize(); +#endif/*_WIN32*/ + return ret; } Modified: trunk/pmplib/frontend/easypmp/cui/option.c =================================================================== --- trunk/pmplib/frontend/easypmp/cui/option.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/frontend/easypmp/cui/option.c 2007-02-27 09:45:39 UTC (rev 397) @@ -79,10 +79,15 @@ fprintf(fp, " -f, --find-missing Correct playlists with corrupt references to media files\n"); fprintf(fp, " -i, --ignore-missing Continue a conversion even if a media file is missing\n"); fprintf(fp, "\n"); + fprintf(fp, "Settlement options (mostly for iPods):\n"); + fprintf(fp, " -s, --settle Settle media files in the location suitable for players\n"); + fprintf(fp, " -x, --unsettle Restore media files in the original location\n"); + fprintf(fp, " -E, --delete-unref Propagate the removal of links to the actual media files\n"); + fprintf(fp, "\n"); fprintf(fp, "Player options:\n"); fprintf(fp, " -l, --list-device Show the list of supported devices and exit\n"); fprintf(fp, " -d, --device Specify a device identifier for the player\n"); - fprintf(fp, " -s, --set=NAME:VALUE Set a variable NAME to VALUE\n"); + fprintf(fp, " -a, --set=NAME:VALUE Set a variable NAME to VALUE\n"); fprintf(fp, "\n"); #ifndef _WIN32 fprintf(fp, "Language-specific options:\n"); @@ -93,7 +98,7 @@ fprintf(fp, "Miscellaneous options:\n"); fprintf(fp, " -v, --version Show version number and exit\n"); fprintf(fp, " -h, --help Show this help message and exit\n"); - fprintf(fp, " -x, --help-variable Show the list of variables and exit\n"); + fprintf(fp, " -H, --help-variable Show the list of variables and exit\n"); } void option_usage_variable(FILE* fp) @@ -109,12 +114,14 @@ void option_init(option_t* opt) { + static const ucs2char_t path[] = {'M','u','s','i','c',0}; memset(opt, 0, sizeof(*opt)); // Set default values here. opt->media_info_source = GMIF_TAG; opt->system_encoding = strdup(ucs2getenc()); opt->music_encoding = strdup("ISO-8859-1"); + opt->path_to_unsettled_music = ucs2dup(path); } void option_finish(option_t* opt) @@ -146,10 +153,10 @@ int this_option_optind = optind ? optind : 1; int option_index = 0; static const struct option long_options[] = { - {"create", no_argument, 0, 'c'}, - {"update", no_argument, 0, 'u'}, - {"source", required_argument, 0, 'z'}, - {"repr", no_argument, 0, 'R'}, + {"create", no_argument, 0, 'c'}, + {"update", no_argument, 0, 'u'}, + {"source", required_argument, 0, 'z'}, + {"repr", no_argument, 0, 'R'}, {"repr-level", required_argument, 0, 'L'}, {"strip-words", required_argument, 0, 't'}, {"playlist", no_argument, 0, 'p'}, @@ -157,22 +164,25 @@ {"reconvert", no_argument, 0, 'r'}, {"find-missing", no_argument, 0, 'f'}, {"ignore-missing", no_argument, 0, 'i'}, + {"settle", no_argument, 0, 's'}, + {"unsettle", no_argument, 0, 'x'}, + {"delete-unref", no_argument, 0, 'E'}, {"list-device", no_argument, 0, 'l'}, - {"device", required_argument, 0, 'd'}, - {"set", required_argument, 0, 's'}, + {"device", required_argument, 0, 'd'}, + {"set", required_argument, 0, 'a'}, #ifndef _WIN32 {"encoding", required_argument, 0, 'e'}, {"tagencoding", required_argument, 0, 'w'}, #endif/*_WIN32*/ - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"help-variable", no_argument, 0, 'x'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"help-variable", no_argument, 0, 'H'}, {NULL, 0, 0, 0} }; #ifndef _WIN32 - int c = getopt_long(argc, argv, "cuz:RL:t:pP:rfild:xs:e:w:vh", long_options, &option_index); + int c = getopt_long(argc, argv, "cuz:RL:t:pP:rfisxEld:a:e:w:vhH", long_options, &option_index); #else - int c = getopt_long(argc, argv, "cuz:RL:t:pP:rfild:xs:vh", long_options, &option_index); + int c = getopt_long(argc, argv, "cuz:RL:t:pP:rfisxEld:a:vhH", long_options, &option_index); #endif/*_WIN32*/ if (c == -1) { break; @@ -230,13 +240,23 @@ opt->verb |= MODE_PLAYLIST_SKIP; break; + case 's': + opt->verb |= MODE_SETTLE; + break; + case 'x': + opt->verb |= MODE_UNSETTLE; + break; + case 'E': + opt->verb |= MODE_REMOVE_UNREF; + break; + case 'l': opt->verb |= MODE_LIST_DEVICES; break; case 'd': strcpy(opt->model, optarg); break; - case 's': + case 'a': if (strncmp("pmp_music:", optarg, 10) == 0) { ucs2free(opt->path_to_pmp_music); opt->path_to_pmp_music = mbsdupucs2(optarg+10); @@ -265,7 +285,7 @@ case 'h': opt->verb |= MODE_HELP; break; - case 'x': + case 'H': opt->verb |= MODE_HELP_VARIABLE; break; case '?': Modified: trunk/pmplib/include/pmplib/filepath.h =================================================================== --- trunk/pmplib/include/pmplib/filepath.h 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/include/pmplib/filepath.h 2007-02-27 09:45:39 UTC (rev 397) @@ -154,6 +154,8 @@ */ FILEPATHAPI const ucs2char_t* filepath_skipadirectory(const ucs2char_t* path); +FILEPATHAPI const ucs2char_t* filepath_extract_extension(const ucs2char_t* path); + /** * Add a file extension to the end of a string. * @@ -277,6 +279,14 @@ FILEPATHAPI int filepath_is_dir(const ucs2char_t *path); /** + * Test if a filepath name is a link to another file. + * + * @param path The filepath name to be tested. + * @retval int True or false. + */ +FILEPATHAPI int filepath_is_link(const ucs2char_t *path); + +/** * Test if a filepath name represents a relative path. * * @param path The filepath name to be tested. @@ -324,6 +334,8 @@ */ FILEPATHAPI uint32_t filepath_size(const ucs2char_t *filename); +FILEPATHAPI int filepath_createdirs(const ucs2char_t* filename); + /** * Remove a file. * @@ -341,6 +353,35 @@ */ FILEPATHAPI int filepath_copyfile(const ucs2char_t* src, const ucs2char_t* dst); +/** + * Move a file. + * + * @param src The name of a source file to be moved. + * @param dst The name of a destination to be created. + * @retval int True if succeeded, false otherwise. + */ +FILEPATHAPI int filepath_movefile(const ucs2char_t* src, const ucs2char_t* dst); + +/** + * Create a link to a file. + * + * @param src The name of a source file to be linked. + * @param link The name of a link file to be created. + * @retval int True if succeeded, false otherwise. + */ +FILEPATHAPI int filepath_linkfile(const ucs2char_t* src, const ucs2char_t* link); + +/** + * Remove a link file. + * + * @param file The name of a link file to be removed. + * @retval int True if succeeded, false otherwise. + */ +FILEPATHAPI int filepath_removelinkfile(const ucs2char_t* file); + +FILEPATHAPI int filepath_followlink(const ucs2char_t* link, ucs2char_t* org); + + /** @} */ Modified: trunk/pmplib/include/pmplib/pmp.h =================================================================== --- trunk/pmplib/include/pmplib/pmp.h 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/include/pmplib/pmp.h 2007-02-27 09:45:39 UTC (rev 397) @@ -203,7 +203,7 @@ ucs2char_t path_to_root[MAX_PATH]; /** Relative path to the system directory from the root. */ ucs2char_t path_to_system[MAX_PATH]; - /** Relative path to the music directory from the root. */ + /** Relative path to the directory for settled music files. */ ucs2char_t path_to_music[MAX_PATH]; /** Relative path to the playlist directory from the root. */ ucs2char_t path_to_playlist[MAX_PATH]; Modified: trunk/pmplib/lib/filepath/filepath.vcproj =================================================================== --- trunk/pmplib/lib/filepath/filepath.vcproj 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/filepath/filepath.vcproj 2007-02-27 09:45:39 UTC (rev 397) @@ -4,6 +4,7 @@ Version="8.00" Name="filepath" ProjectGUID="{AA8DA82B-C209-4ABE-ABA1-22352962426D}" + RootNamespace="filepath" Keyword="Win32Proj" > <Platforms> Modified: trunk/pmplib/lib/filepath/filepath_win32.c =================================================================== --- trunk/pmplib/lib/filepath/filepath_win32.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/filepath/filepath_win32.c 2007-02-27 09:45:39 UTC (rev 397) @@ -31,11 +31,15 @@ #include <sys/stat.h> #include <windows.h> +#include <shlobj.h> #include <shlwapi.h> +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "shlwapi.lib") #include <pmplib/filepath.h> -#pragma comment(lib, "shlwapi.lib") +static const ucs2char_t ucs2cs_link_ext[] = {'.','m','3','u','8',0}; + int find_file(const ucs2char_t* path, int recursive, filepath_findfile_callback callback, void *instance) { HANDLE hFile = INVALID_HANDLE_VALUE; @@ -133,6 +137,12 @@ return p ? p+1 : NULL; } +const ucs2char_t* filepath_extract_extension(const ucs2char_t* path) +{ + ucs2char_t* p = ucs2rchr(path, '.'); + return p ? p : path; +} + void filepath_strippath(ucs2char_t* path) { PathStripPathW(path); @@ -206,17 +216,88 @@ return ret; } +int filepath_createdirs(const ucs2char_t* filename) +{ + return SHCreateDirectoryExW(NULL, (LPCWSTR)filename, NULL) == ERROR_SUCCESS ? 0 : 1; +} + int filepath_copyfile(const ucs2char_t* src, const ucs2char_t* dst) { return CopyFileW(src, dst, FALSE) ? 0 : -1; } +int filepath_movefile(const ucs2char_t* src, const ucs2char_t* dst) +{ + return MoveFileW(src, dst) ? 0 : -1; +} + int filepath_removefile(const ucs2char_t* file) { SetFileAttributesW(file, FILE_ATTRIBUTE_NORMAL); return DeleteFileW(file); } +int filepath_linkfile(const ucs2char_t* target, const ucs2char_t* link) +{ + /* Create a m3u8 playlist with the target file. */ + FILE *fp = NULL; + ucs2char_t* _link = alloca(sizeof(ucs2char_t) * (ucs2len(link) + ucs2len(ucs2cs_link_ext) + 1)); + + /* Add ".m3u8" extension. */ + ucs2cpy(_link, link); + ucs2cat(_link, ucs2cs_link_ext); + + /* Write the */ + fp = ucs2fopen(_link, "w"); + if (fp) { + char *utf8 = ucs2duputf8((const ucs2char_t*)PathSkipRootW((LPCWSTR)target)); + fputc('\\', fp); + fputs(utf8, fp); + ucs2free(utf8); + fclose(fp); + return 0; + } else { + return -1; + } +} + +int filepath_followlink(const ucs2char_t* link, ucs2char_t* org) +{ + /* Create a m3u8 playlist with the target file. */ + FILE *fp = NULL; + ucs2char_t* _link = (ucs2char_t*)alloca( + sizeof(ucs2char_t) * (ucs2len(link) + ucs2len(ucs2cs_link_ext) + 1)); + + /* Add ".m3u8" extension. */ + ucs2cpy(_link, link); + ucs2cat(_link, ucs2cs_link_ext); + + /* Write the */ + fp = ucs2fopen(_link, "r"); + if (fp) { + char buff[MAX_PATH*4]; + ucs2char_t target[MAX_PATH]; + fgets(buff, sizeof(buff)-1, fp); + utf8toucs2(target, MAX_PATH, buff, strlen(buff)+1); + filepath_combinepath(org, MAX_PATH, link, target); + return 0; + } else { + return -1; + } +} + +int filepath_removelinkfile(const ucs2char_t* file) +{ + ucs2char_t* _link = (ucs2char_t*)alloca( + sizeof(ucs2char_t) * (ucs2len(file) + ucs2len(ucs2cs_link_ext) + 1)); + + /* Add ".m3u8" extension. */ + ucs2cpy(_link, file); + ucs2cat(_link, ucs2cs_link_ext); + + return filepath_removefile(_link); +} + int filepath_encode(ucs2char_t* path) { /* Does nothing for WIN32. */ @@ -260,3 +341,8 @@ } return 0; } + +int filepath_is_link(const ucs2char_t *path) +{ + return filepath_hasext(path, ucs2cs_link_ext); +} Modified: trunk/pmplib/lib/pmp_ipod/pmp_ipod.c =================================================================== --- trunk/pmplib/lib/pmp_ipod/pmp_ipod.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/pmp_ipod/pmp_ipod.c 2007-02-27 09:45:39 UTC (rev 397) @@ -415,7 +415,6 @@ if (pmp->flag & PMPOF_MUSIC_DB_WRITE) { ipod_set(&ipod, pmpmi->records, pmpmi->num_records, pmpmi->playlists, pmpmi->num_playlists); ipod_write(&ipod, itunesdb); - ipod_dump(&ipod, stdout); } exit_this: Modified: trunk/pmplib/lib/pmp_ipod/pmp_ipod.vcproj =================================================================== --- trunk/pmplib/lib/pmp_ipod/pmp_ipod.vcproj 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/pmp_ipod/pmp_ipod.vcproj 2007-02-27 09:45:39 UTC (rev 397) @@ -193,6 +193,10 @@ > </File> <File + RelativePath=".\settledb.c" + > + </File> + <File RelativePath=".\util.c" > </File> @@ -215,6 +219,10 @@ > </File> <File + RelativePath=".\settledb.h" + > + </File> + <File RelativePath=".\util.h" > </File> Modified: trunk/pmplib/lib/ucs2/ucs2char.c =================================================================== --- trunk/pmplib/lib/ucs2/ucs2char.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/ucs2/ucs2char.c 2007-02-27 09:45:39 UTC (rev 397) @@ -410,3 +410,13 @@ } return dst; } + +char *ucs2duputf8(const ucs2char_t *ucs2str) +{ + size_t mbs_size = ucs2toutf8(NULL, 0, ucs2str, ucs2len(ucs2str)) + 1; + char* dst = (char *)malloc(mbs_size * sizeof(char)); + if (dst) { + ucs2toutf8(dst, mbs_size, ucs2str, ucs2len(ucs2str)+1); + } + return dst; +} Modified: trunk/pmplib/lib/ucs2/ucs2char_win32.c =================================================================== --- trunk/pmplib/lib/ucs2/ucs2char_win32.c 2007-02-25 14:16:32 UTC (rev 396) +++ trunk/pmplib/lib/ucs2/ucs2char_win32.c 2007-02-27 09:45:39 UTC (rev 397) @@ -99,8 +99,13 @@ return MultiByteToWideChar(CP_UTF8, 0, mbstr, (int)mbs_len, ucs2str, (int)ucs_len); } +size_t ucs2toutf8(char *mbstr, size_t mbs_len, const ucs2char_t *ucs2str, size_t ucs_len) +{ + return WideCharToMultiByte(CP_UTF8, 0, ucs2str, (int)ucs_len, mbstr, (int)mbs_len, NULL, NULL); +} + wchar_t* mbsdupwcs(const char *mbstr) { /* Just call mbsdupucs2(). */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |