1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "string-extra.h"
27 #include "buffering.h"
30 #include "filefuncs.h"
34 /* Define LOGF_ENABLE to enable logf output in this file */
35 /*#define LOGF_ENABLE*/
38 #if defined(HAVE_JPEG) || defined(PLUGIN)
39 #define USE_JPEG_COVER
42 /* Strip filename from a full path
44 * buf - buffer to extract directory to.
45 * buf_size - size of buffer.
46 * fullpath - fullpath to extract from.
48 * Split the directory part of the given fullpath and store it in buf
49 * (including last '/').
50 * The function return parameter is a pointer to the filename
51 * inside the given fullpath.
53 static char* strip_filename(char* buf
, int buf_size
, const char* fullpath
)
58 if (!buf
|| buf_size
<= 0 || !fullpath
)
61 /* if 'fullpath' is only a filename return immediately */
62 sep
= strrchr(fullpath
, '/');
66 return (char*)fullpath
;
69 len
= MIN(sep
- fullpath
+ 1, buf_size
- 1);
70 strlcpy(buf
, fullpath
, len
+ 1);
74 /* Make sure part of path only contain chars valid for a FAT32 long name.
75 * Double quotes are replaced with single quotes, other unsupported chars
76 * are replaced with an underscore.
78 * path - path to modify.
79 * offset - where in path to start checking.
80 * count - number of chars to check.
82 static void fix_path_part(char* path
, int offset
, int count
)
84 static const char invalid_chars
[] = "*/:<>?\\|";
89 for (i
= 0; i
<= count
; i
++, path
++)
95 else if (strchr(invalid_chars
, *path
))
100 #ifdef USE_JPEG_COVER
101 static const char * extensions
[] = { "jpeg", "jpg", "bmp" };
102 static const unsigned char extension_lens
[] = { 4, 3, 3 };
103 /* Try checking for several file extensions, return true if a file is found and
104 * leaving the path modified to include the matching extension.
106 static bool try_exts(char *path
, int len
)
109 for (i
= 0; i
< 3; i
++)
111 if (extension_lens
[i
] + len
> MAX_PATH
)
113 strcpy(path
+ len
, extensions
[i
]);
114 if (file_exists(path
))
122 #define try_exts(path, len) file_exists(path)
125 /* Look for the first matching album art bitmap in the following list:
126 * ./<trackname><size>.{jpeg,jpg,bmp}
127 * ./<albumname><size>.{jpeg,jpg,bmp}
129 * ../<albumname><size>.{jpeg,jpg,bmp}
130 * ../cover<size>.{jpeg,jpg,bmp}
131 * ROCKBOX_DIR/albumart/<artist>-<albumname><size>.{jpeg,jpg,bmp}
132 * <size> is the value of the size_string parameter, <trackname> and
133 * <albumname> are read from the ID3 metadata.
134 * If a matching bitmap is found, its filename is stored in buf.
135 * Return value is true if a bitmap was found, false otherwise.
137 * If the first symbol in size_string is a colon (e.g. ":100x100")
138 * then the colon is skipped ("100x100" will be used) and the track
139 * specific image (./<trackname><size>.bmp) is tried last instead of first.
141 bool search_albumart_files(const struct mp3entry
*id3
, const char *size_string
,
142 char *buf
, int buflen
)
144 char path
[MAX_PATH
+ 1];
145 char dir
[MAX_PATH
+ 1];
149 const char *trackname
;
158 trackname
= id3
->path
;
160 if (strcmp(trackname
, "No file!") == 0)
163 if (*size_string
== ':')
169 strip_filename(dir
, sizeof(dir
), trackname
);
170 dirlen
= strlen(dir
);
171 albumlen
= id3
->album
? strlen(id3
->album
) : 0;
173 for(pass
= 0; pass
< 2 - track_first
; pass
++)
175 if (track_first
|| pass
)
177 /* the first file we look for is one specific to the
179 strip_extension(path
, sizeof(path
) - strlen(size_string
) - 4,
181 strcat(path
, size_string
);
182 strcat(path
, "." EXT
);
183 #ifdef USE_JPEG_COVER
184 pathlen
= strlen(path
);
186 found
= try_exts(path
, pathlen
);
190 if (!found
&& albumlen
> 0)
192 /* if it doesn't exist,
193 * we look for a file specific to the track's album name */
194 pathlen
= snprintf(path
, sizeof(path
),
195 "%s%s%s." EXT
, dir
, id3
->album
, size_string
);
196 fix_path_part(path
, dirlen
, albumlen
);
197 found
= try_exts(path
, pathlen
);
202 /* if it still doesn't exist, we look for a generic file */
203 pathlen
= snprintf(path
, sizeof(path
),
204 "%scover%s." EXT
, dir
, size_string
);
205 found
= try_exts(path
, pathlen
);
208 #ifdef USE_JPEG_COVER
209 if (!found
&& !*size_string
)
211 snprintf (path
, sizeof(path
), "%sfolder.jpg", dir
);
212 found
= file_exists(path
);
216 artist
= id3
->albumartist
!= NULL
? id3
->albumartist
: id3
->artist
;
218 if (!found
&& artist
&& id3
->album
)
220 /* look in the albumart subdir of .rockbox */
221 pathlen
= snprintf(path
, sizeof(path
),
222 ROCKBOX_DIR
"/albumart/%s-%s%s." EXT
,
226 fix_path_part(path
, strlen(ROCKBOX_DIR
"/albumart/"), MAX_PATH
);
227 found
= try_exts(path
, pathlen
);
232 /* if it still doesn't exist,
233 * we continue to search in the parent directory */
235 path
[dirlen
- 1] = 0;
236 strip_filename(dir
, sizeof(dir
), path
);
237 dirlen
= strlen(dir
);
240 /* only try parent if there is one */
243 if (!found
&& albumlen
> 0)
245 /* we look in the parent directory
246 * for a file specific to the track's album name */
247 pathlen
= snprintf(path
, sizeof(path
),
248 "%s%s%s." EXT
, dir
, id3
->album
, size_string
);
249 fix_path_part(path
, dirlen
, albumlen
);
250 found
= try_exts(path
, pathlen
);
255 /* if it still doesn't exist, we look in the parent directory
256 * for a generic file */
257 pathlen
= snprintf(path
, sizeof(path
),
258 "%scover%s." EXT
, dir
, size_string
);
259 found
= try_exts(path
, pathlen
);
269 strlcpy(buf
, path
, buflen
);
270 logf("Album art found: %s", path
);
275 /* Look for albumart bitmap in the same dir as the track and in its parent dir.
276 * Stores the found filename in the buf parameter.
277 * Returns true if a bitmap was found, false otherwise */
278 bool find_albumart(const struct mp3entry
*id3
, char *buf
, int buflen
,
279 const struct dim
*dim
)
285 logf("Looking for album art for %s", id3
->path
);
287 /* Write the size string, e.g. ".100x100". */
288 snprintf(size_string
, sizeof(size_string
), ".%dx%d",
289 dim
->width
, dim
->height
);
291 /* First we look for a bitmap of the right size */
292 if (search_albumart_files(id3
, size_string
, buf
, buflen
))
295 /* Then we look for generic bitmaps */
297 return search_albumart_files(id3
, size_string
, buf
, buflen
);
300 /* Draw the album art bitmap from the given handle ID onto the given WPS.
301 Call with clear = true to clear the bitmap instead of drawing it. */
302 void draw_album_art(struct gui_wps
*gwps
, int handle_id
, bool clear
)
304 if (!gwps
|| !gwps
->data
|| !gwps
->display
|| handle_id
< 0)
307 struct wps_data
*data
= gwps
->data
;
308 struct skin_albumart
*aa
= SKINOFFSETTOPTR(get_skin_buffer(data
), data
->albumart
);
314 if (bufgetdata(handle_id
, 0, (void *)&bmp
) <= 0)
319 short width
= bmp
->width
;
320 short height
= bmp
->height
;
324 /* Crop if the bitmap is too wide */
325 width
= MIN(bmp
->width
, aa
->width
);
328 if (aa
->xalign
& WPS_ALBUMART_ALIGN_RIGHT
)
329 x
+= aa
->width
- width
;
330 else if (aa
->xalign
& WPS_ALBUMART_ALIGN_CENTER
)
331 x
+= (aa
->width
- width
) / 2;
336 /* Crop if the bitmap is too high */
337 height
= MIN(bmp
->height
, aa
->height
);
340 if (aa
->yalign
& WPS_ALBUMART_ALIGN_BOTTOM
)
341 y
+= aa
->height
- height
;
342 else if (aa
->yalign
& WPS_ALBUMART_ALIGN_CENTER
)
343 y
+= (aa
->height
- height
) / 2;
348 /* Draw the bitmap */
349 gwps
->display
->bitmap_part((fb_data
*)bmp
->data
, 0, 0,
350 STRIDE(gwps
->display
->screen_type
,
351 bmp
->width
, bmp
->height
),
352 x
, y
, width
, height
);
353 #ifdef HAVE_LCD_INVERT
354 if (global_settings
.invert
) {
355 gwps
->display
->set_drawmode(DRMODE_COMPLEMENT
);
356 gwps
->display
->fillrect(x
, y
, width
, height
);
357 gwps
->display
->set_drawmode(DRMODE_SOLID
);
363 /* Clear the bitmap */
364 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
365 gwps
->display
->fillrect(x
, y
, width
, height
);
366 gwps
->display
->set_drawmode(DRMODE_SOLID
);