diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/codecs.c | 1 | ||||
-rw-r--r-- | apps/codecs.h | 4 | ||||
-rw-r--r-- | apps/codecs/lib/xxx2wav.c | 6 | ||||
-rw-r--r-- | apps/codecs/vorbis.c | 149 | ||||
-rw-r--r-- | apps/metadata.c | 223 |
5 files changed, 350 insertions, 33 deletions
diff --git a/apps/codecs.c b/apps/codecs.c index 16338c263c..8b4f9b5f32 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -243,6 +243,7 @@ struct codec_api ci = { | |||
243 | logf, | 243 | logf, |
244 | #endif | 244 | #endif |
245 | 245 | ||
246 | memchr, | ||
246 | }; | 247 | }; |
247 | 248 | ||
248 | int codec_load_ram(char* codecptr, size_t size, void* ptr2, size_t bufwrap) | 249 | int codec_load_ram(char* codecptr, size_t size, void* ptr2, size_t bufwrap) |
diff --git a/apps/codecs.h b/apps/codecs.h index 1a03139be8..5878ca9185 100644 --- a/apps/codecs.h +++ b/apps/codecs.h | |||
@@ -240,7 +240,7 @@ struct codec_api { | |||
240 | #endif | 240 | #endif |
241 | #if CONFIG_HWCODEC == MASNONE | 241 | #if CONFIG_HWCODEC == MASNONE |
242 | void (*pcm_play_data)(const unsigned char *start, int size, | 242 | void (*pcm_play_data)(const unsigned char *start, int size, |
243 | void (*get_more)(unsigned char** start, long*size)); | 243 | void (*get_more)(unsigned char** start, long*size)); |
244 | void (*pcm_play_stop)(void); | 244 | void (*pcm_play_stop)(void); |
245 | void (*pcm_set_frequency)(unsigned int frequency); | 245 | void (*pcm_set_frequency)(unsigned int frequency); |
246 | bool (*pcm_is_playing)(void); | 246 | bool (*pcm_is_playing)(void); |
@@ -326,6 +326,8 @@ struct codec_api { | |||
326 | #ifdef ROCKBOX_HAS_LOGF | 326 | #ifdef ROCKBOX_HAS_LOGF |
327 | void (*logf)(const char *fmt, ...); | 327 | void (*logf)(const char *fmt, ...); |
328 | #endif | 328 | #endif |
329 | |||
330 | void *(*memchr)(const void *s1, int c, size_t n); | ||
329 | }; | 331 | }; |
330 | 332 | ||
331 | /* defined by the codec loader (codec.c) */ | 333 | /* defined by the codec loader (codec.c) */ |
diff --git a/apps/codecs/lib/xxx2wav.c b/apps/codecs/lib/xxx2wav.c index 1323e07475..b437ca56db 100644 --- a/apps/codecs/lib/xxx2wav.c +++ b/apps/codecs/lib/xxx2wav.c | |||
@@ -91,11 +91,7 @@ int memcmp(const void *s1, const void *s2, size_t n) { | |||
91 | } | 91 | } |
92 | 92 | ||
93 | void* memchr(const void *s, int c, size_t n) { | 93 | void* memchr(const void *s, int c, size_t n) { |
94 | /* TO DO: Implement for Tremor */ | 94 | return(local_rb->memchr(s,c,n)); |
95 | (void)s; | ||
96 | (void)c; | ||
97 | (void)n; | ||
98 | return(NULL); | ||
99 | } | 95 | } |
100 | 96 | ||
101 | void* memmove(const void *s1, const void *s2, size_t n) { | 97 | void* memmove(const void *s1, const void *s2, size_t n) { |
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c index 9afeb053e1..946f2f9377 100644 --- a/apps/codecs/vorbis.c +++ b/apps/codecs/vorbis.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "codecs.h" | 20 | #include "codecs.h" |
21 | 21 | ||
22 | #include "Tremor/ivorbisfile.h" | 22 | #include "Tremor/ivorbisfile.h" |
23 | #include "Tremor/ogg.h" | ||
23 | #include "playback.h" | 24 | #include "playback.h" |
24 | #include "dsp.h" | 25 | #include "dsp.h" |
25 | #include "lib/codeclib.h" | 26 | #include "lib/codeclib.h" |
@@ -51,15 +52,30 @@ size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) | |||
51 | return rb->read_filebuf(ptr, nmemb*size); | 52 | return rb->read_filebuf(ptr, nmemb*size); |
52 | } | 53 | } |
53 | 54 | ||
54 | int seek_handler(void *datasource, ogg_int64_t offset, int whence) | 55 | int initial_seek_handler(void *datasource, ogg_int64_t offset, int whence) { |
55 | { | ||
56 | /* We are not seekable at the moment */ | ||
57 | (void)datasource; | 56 | (void)datasource; |
58 | (void)offset; | 57 | (void)offset; |
59 | (void)whence; | 58 | (void)whence; |
60 | return -1; | 59 | return -1; |
61 | } | 60 | } |
62 | 61 | ||
62 | int seek_handler(void *datasource, ogg_int64_t offset, int whence) | ||
63 | { | ||
64 | (void)datasource; | ||
65 | |||
66 | if ( whence == SEEK_CUR ) { | ||
67 | offset += rb->curpos; | ||
68 | } else if ( whence == SEEK_END ) { | ||
69 | offset += rb->filesize; | ||
70 | } | ||
71 | |||
72 | if (rb->seek_buffer(offset)) { | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | return -1; | ||
77 | } | ||
78 | |||
63 | int close_handler(void *datasource) | 79 | int close_handler(void *datasource) |
64 | { | 80 | { |
65 | (void)datasource; | 81 | (void)datasource; |
@@ -93,22 +109,26 @@ enum codec_status codec_start(struct codec_api* api) | |||
93 | long n; | 109 | long n; |
94 | int current_section; | 110 | int current_section; |
95 | int eof; | 111 | int eof; |
112 | ogg_int64_t vf_offsets[2]; | ||
113 | ogg_int64_t vf_dataoffsets; | ||
114 | ogg_uint32_t vf_serialnos; | ||
115 | ogg_int64_t vf_pcmlengths[2]; | ||
116 | int current_stereo_mode = -1; | ||
96 | 117 | ||
97 | TEST_CODEC_API(api); | 118 | TEST_CODEC_API(api); |
98 | 119 | ||
99 | /* if you are using a global api pointer, don't forget to copy it! | 120 | /* if you are using a global api pointer, don't forget to copy it! |
100 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ | 121 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ |
101 | rb = api; | 122 | rb = api; |
102 | 123 | ||
103 | #ifdef USE_IRAM | 124 | #ifdef USE_IRAM |
104 | rb->memcpy(iramstart, iramcopy, iramend-iramstart); | 125 | rb->memcpy(iramstart, iramcopy, iramend-iramstart); |
105 | #endif | 126 | #endif |
106 | 127 | ||
107 | rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); | 128 | rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); |
108 | rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64)); | 129 | rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64)); |
109 | 130 | ||
110 | rb->configure(DSP_DITHER, (bool *)false); | 131 | rb->configure(DSP_DITHER, (bool *)false); |
111 | rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
112 | rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | 132 | rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); |
113 | 133 | ||
114 | /* We need to flush reserver memory every track load. */ | 134 | /* We need to flush reserver memory every track load. */ |
@@ -129,44 +149,123 @@ enum codec_status codec_start(struct codec_api* api) | |||
129 | 149 | ||
130 | /* Create a decoder instance */ | 150 | /* Create a decoder instance */ |
131 | callbacks.read_func=read_handler; | 151 | callbacks.read_func=read_handler; |
132 | callbacks.seek_func=seek_handler; | 152 | callbacks.seek_func=initial_seek_handler; |
133 | callbacks.tell_func=tell_handler; | 153 | callbacks.tell_func=tell_handler; |
134 | callbacks.close_func=close_handler; | 154 | callbacks.close_func=close_handler; |
135 | 155 | ||
156 | /* Open a non-seekable stream */ | ||
136 | error=ov_open_callbacks(rb,&vf,NULL,0,callbacks); | 157 | error=ov_open_callbacks(rb,&vf,NULL,0,callbacks); |
137 | 158 | ||
159 | /* If the non-seekable open was successful, we need to supply the missing | ||
160 | * data to make it seekable. This is a hack, but it's reasonable since we | ||
161 | * don't want to read the whole file into the buffer before we start | ||
162 | * playing. Using Tremor's seekable open routine would cause us to do | ||
163 | * this, so we pretend not to be seekable at first. Then we fill in the | ||
164 | * missing fields of vf with 1) information in rb->id3, and 2) info | ||
165 | * obtained by Tremor in the above ov_open call. | ||
166 | * | ||
167 | * Note that this assumes there is only ONE logical Vorbis bitstream in our | ||
168 | * physical Ogg bitstream. This is verified in metadata.c, well before we | ||
169 | * get here. | ||
170 | */ | ||
171 | if ( !error ) { | ||
172 | //rb->logf("no error"); | ||
173 | /* FIXME Should these be dynamically allocated? */ | ||
174 | vf.offsets = vf_offsets; | ||
175 | vf.dataoffsets = &vf_dataoffsets; | ||
176 | vf.serialnos = &vf_serialnos; | ||
177 | vf.pcmlengths = vf_pcmlengths; | ||
178 | |||
179 | vf.offsets[0] = 0; | ||
180 | vf.offsets[1] = rb->id3->filesize; | ||
181 | vf.dataoffsets[0] = vf.offset; | ||
182 | vf.pcmlengths[0] = 0; | ||
183 | vf.pcmlengths[1] = rb->id3->samples; | ||
184 | vf.serialnos[0] = vf.current_serialno; | ||
185 | vf.callbacks.seek_func=seek_handler; | ||
186 | vf.seekable = 1; | ||
187 | vf.offset = 58; /* length of Ogg header */ | ||
188 | vf.end = rb->id3->filesize; | ||
189 | vf.ready_state = OPENED; | ||
190 | vf.links = 1; | ||
191 | |||
192 | /*if(ov_raw_seek(&vf,0)){ | ||
193 | rb->logf("seek err"); | ||
194 | } | ||
195 | */ | ||
196 | |||
197 | } else { | ||
198 | //rb->logf("ov_open: %d", error); | ||
199 | } | ||
200 | |||
138 | vi=ov_info(&vf,-1); | 201 | vi=ov_info(&vf,-1); |
139 | 202 | ||
140 | if (vi==NULL) { | 203 | if (vi==NULL) { |
141 | // rb->splash(HZ*2, true, "Vorbis Error"); | 204 | //rb->splash(HZ*2, true, "Vorbis Error"); |
142 | return CODEC_ERROR; | 205 | return CODEC_ERROR; |
143 | } | 206 | } |
144 | 207 | ||
208 | rb->configure(DSP_SET_FREQUENCY, (int *)rb->id3->frequency); | ||
209 | |||
210 | if (vi->channels == 2) { | ||
211 | if (current_stereo_mode != STEREO_INTERLEAVED) { | ||
212 | rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
213 | current_stereo_mode = STEREO_INTERLEAVED; | ||
214 | } | ||
215 | } else if (vi->channels == 1) { | ||
216 | if (current_stereo_mode != STEREO_MONO) { | ||
217 | rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO); | ||
218 | current_stereo_mode = STEREO_MONO; | ||
219 | } | ||
220 | } | ||
221 | |||
145 | eof=0; | 222 | eof=0; |
223 | rb->yield(); | ||
146 | while (!eof) { | 224 | while (!eof) { |
225 | if (rb->stop_codec || rb->reload_codec) | ||
226 | break ; | ||
227 | |||
228 | if (rb->seek_time) { | ||
229 | |||
230 | if (ov_time_seek(&vf, rb->seek_time)) { | ||
231 | //rb->logf("ov_time_seek failed"); | ||
232 | } | ||
233 | rb->seek_time = 0; | ||
234 | } | ||
235 | |||
147 | /* Read host-endian signed 16 bit PCM samples */ | 236 | /* Read host-endian signed 16 bit PCM samples */ |
148 | n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),¤t_section); | 237 | n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),¤t_section); |
149 | 238 | ||
150 | if (n==0) { | 239 | if (n==0) { |
151 | eof=1; | 240 | eof=1; |
152 | } | 241 | } else if (n < 0) { |
153 | else if (n < 0) { | ||
154 | DEBUGF("Error decoding frame\n"); | 242 | DEBUGF("Error decoding frame\n"); |
155 | } else { | 243 | } else { |
156 | rb->yield(); | 244 | while (!rb->audiobuffer_insert(pcmbuf, n)) { |
157 | if (rb->stop_codec || rb->reload_codec) | ||
158 | break ; | ||
159 | |||
160 | while (!rb->audiobuffer_insert(pcmbuf, n)) | ||
161 | rb->yield(); | 245 | rb->yield(); |
162 | 246 | if ( rb->seek_time ) { | |
163 | rb->set_elapsed(ov_time_tell(&vf)); | 247 | /* Hmmm, a seek was requested. Throw out the |
248 | * buffer and go back to the top of the loop. | ||
249 | */ | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | if ( !rb->seek_time ) { | ||
254 | rb->set_elapsed(ov_time_tell(&vf)); | ||
255 | rb->yield(); | ||
256 | } | ||
164 | } | 257 | } |
165 | } | 258 | } |
166 | |||
167 | if (rb->request_next_track()) | ||
168 | goto next_track; | ||
169 | 259 | ||
260 | if (rb->request_next_track()) { | ||
261 | /* Clean things up for the next track */ | ||
262 | vf.dataoffsets = NULL; | ||
263 | vf.offsets = NULL; | ||
264 | vf.serialnos = NULL; | ||
265 | vf.pcmlengths = NULL; | ||
266 | ov_clear(&vf); | ||
267 | goto next_track; | ||
268 | } | ||
269 | |||
170 | return CODEC_OK; | 270 | return CODEC_OK; |
171 | } | 271 | } |
172 | |||
diff --git a/apps/metadata.c b/apps/metadata.c index f59917ceb8..6ba2331d26 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -91,9 +91,14 @@ const long wavpack_sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, | |||
91 | 91 | ||
92 | static bool get_apetag_info (struct mp3entry *entry, int fd); | 92 | static bool get_apetag_info (struct mp3entry *entry, int fd); |
93 | 93 | ||
94 | static bool get_vorbis_comments (struct mp3entry *entry, int fd); | ||
95 | |||
96 | static void little_endian_to_native (void *data, char *format); | ||
97 | |||
94 | bool get_metadata(struct track_info* track, int fd, const char* trackname, | 98 | bool get_metadata(struct track_info* track, int fd, const char* trackname, |
95 | bool v1first) { | 99 | bool v1first) { |
96 | unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes; | 100 | unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes; |
101 | unsigned long serialno=0, last_serialno=0; | ||
97 | int bytesperframe; | 102 | int bytesperframe; |
98 | unsigned char* buf; | 103 | unsigned char* buf; |
99 | int i,j,eof; | 104 | int i,j,eof; |
@@ -259,13 +264,29 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
259 | return(false); | 264 | return(false); |
260 | } | 265 | } |
261 | 266 | ||
267 | /* We need to ensure the serial number from this page is the | ||
268 | * same as the one from the last page (since we only support | ||
269 | * a single bitstream). | ||
270 | */ | ||
271 | serialno = buf[14]|(buf[15]<<8)|(buf[16]<<16)|(buf[17]<<24); | ||
272 | |||
262 | /* Ogg stores integers in little-endian format. */ | 273 | /* Ogg stores integers in little-endian format. */ |
263 | track->id3.filesize=filesize(fd); | 274 | track->id3.filesize=filesize(fd); |
264 | track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24); | 275 | track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24); |
265 | channels=buf[39]; | 276 | channels=buf[39]; |
266 | 277 | ||
278 | if ( !get_vorbis_comments(&(track->id3), fd) ) { | ||
279 | logf("get_vorbis_comments failed"); | ||
280 | return(false); | ||
281 | } | ||
282 | |||
283 | /* Set id3 genre to something bogus, otherwise vorbis tracks | ||
284 | * without genre tags will show up as 'Blues' | ||
285 | */ | ||
286 | track->id3.genre=255; | ||
287 | |||
267 | /* We now need to search for the last page in the file - identified by | 288 | /* We now need to search for the last page in the file - identified by |
268 | by ('O','g','g','S',0) and retrieve totalsamples */ | 289 | by ('O','g','g','S',0) and retrieve totalsamples */ |
269 | 290 | ||
270 | lseek(fd, -32*1024, SEEK_END); | 291 | lseek(fd, -32*1024, SEEK_END); |
271 | eof=0; | 292 | eof=0; |
@@ -283,8 +304,9 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
283 | i=0; | 304 | i=0; |
284 | while (i < (j-5)) { | 305 | while (i < (j-5)) { |
285 | if (memcmp(&buf[i],"OggS",5)==0) { | 306 | if (memcmp(&buf[i],"OggS",5)==0) { |
286 | if (i < (j-10)) { | 307 | if (i < (j-17)) { |
287 | totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24); | 308 | totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24); |
309 | last_serialno=(buf[i+14])|(buf[i+15]<<8)|(buf[i+16]<<16)|(buf[i+17]<<24); | ||
288 | j=0; /* We can discard the rest of the buffer */ | 310 | j=0; /* We can discard the rest of the buffer */ |
289 | } else { | 311 | } else { |
290 | break; | 312 | break; |
@@ -300,7 +322,18 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
300 | j=0; | 322 | j=0; |
301 | } | 323 | } |
302 | } | 324 | } |
325 | |||
326 | /* This file has mutiple vorbis bitstreams (or is corrupt) */ | ||
327 | /* FIXME we should display an error here */ | ||
328 | if (serialno != last_serialno) { | ||
329 | track->taginfo_ready=false; | ||
330 | logf("serialno mismatch"); | ||
331 | logf("%ld", serialno); | ||
332 | logf("%ld", last_serialno); | ||
333 | return false; | ||
334 | } | ||
303 | 335 | ||
336 | track->id3.samples=totalsamples; | ||
304 | track->id3.length=(totalsamples/track->id3.frequency)*1000; | 337 | track->id3.length=(totalsamples/track->id3.frequency)*1000; |
305 | 338 | ||
306 | /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */ | 339 | /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */ |
@@ -712,3 +745,189 @@ static void UTF8ToAnsi (unsigned char *pUTF8) | |||
712 | *pAnsi = 0; | 745 | *pAnsi = 0; |
713 | } | 746 | } |
714 | 747 | ||
748 | /* This function extracts the information stored in the Vorbis comment header | ||
749 | * and stores it in id3v2buf of the current track. Currently the combined | ||
750 | * lengths of title, genre, album, and artist must be no longer than 296 bytes | ||
751 | * (the remaining 4 bytes are the null bytes at the end of the strings). This | ||
752 | * is wrong, since vorbis comments can be up to 2^32 - 1 bytes long. In | ||
753 | * practice I don't think this limitation will cause a problem. | ||
754 | * | ||
755 | * According to the docs, a vorbis bitstream *must* have a comment packet even | ||
756 | * if that packet is empty. Therefore if this function returns false the | ||
757 | * bitstream is corrupt and shouldn't be used. | ||
758 | * | ||
759 | * Additionally, vorbis comments *may* take up more than one Ogg page, and this | ||
760 | * only looks at the first page of comments. | ||
761 | */ | ||
762 | static bool get_vorbis_comments (struct mp3entry *entry, int fd) | ||
763 | { | ||
764 | int vendor_length; | ||
765 | int comment_count; | ||
766 | int comment_length; | ||
767 | int i = 0; | ||
768 | unsigned char temp[300]; | ||
769 | int buffer_remaining = sizeof(entry->id3v2buf); | ||
770 | char *buffer = entry->id3v2buf; | ||
771 | char **p = NULL; | ||
772 | int segments; | ||
773 | int packet_remaining = 0; | ||
774 | |||
775 | /* Comments are in second Ogg page */ | ||
776 | if ( lseek(fd, 58, SEEK_SET) < 0 ) { | ||
777 | return false; | ||
778 | } | ||
779 | |||
780 | /* Minimum header length for Ogg pages is 27 */ | ||
781 | if (read(fd, temp, 27) < 27) { | ||
782 | return false; | ||
783 | } | ||
784 | |||
785 | if (memcmp(temp,"OggS",4)!=0) { | ||
786 | logf("1: Not an Ogg Vorbis file"); | ||
787 | return(false); | ||
788 | } | ||
789 | |||
790 | segments=temp[26]; | ||
791 | /* read in segment table */ | ||
792 | if (read(fd, temp, segments) < segments) { | ||
793 | return false; | ||
794 | } | ||
795 | |||
796 | /* The second packet in a vorbis stream is the comment packet. It *may* | ||
797 | * extend beyond the second page, but usually does not. Here we find the | ||
798 | * length of the comment packet (or the rest of the page if the comment | ||
799 | * packet extends to the third page). | ||
800 | */ | ||
801 | for (i = 0; i < segments; i++) { | ||
802 | packet_remaining += temp[i]; | ||
803 | /* The last segment of a packet is always < 255 bytes */ | ||
804 | if (temp[i] < 255) { | ||
805 | break; | ||
806 | } | ||
807 | } | ||
808 | |||
809 | /* Now read in packet header (type and id string) */ | ||
810 | if(read(fd, temp, 7) < 7) { | ||
811 | return false; | ||
812 | } | ||
813 | |||
814 | /* The first byte of a packet is the packet type; comment packets are | ||
815 | * type 3. | ||
816 | */ | ||
817 | if ((temp[0] != 3) || (memcmp(temp + 1,"vorbis",6)!=0)) { | ||
818 | logf("Not a vorbis comment packet"); | ||
819 | return false; | ||
820 | } | ||
821 | |||
822 | packet_remaining -= 7; | ||
823 | |||
824 | |||
825 | /* We've read in all header info, now start reading comments */ | ||
826 | |||
827 | if (read(fd, &vendor_length, 4) < 4) { | ||
828 | return false; | ||
829 | } | ||
830 | little_endian_to_native(&vendor_length, "L"); | ||
831 | lseek(fd, vendor_length, SEEK_CUR); | ||
832 | |||
833 | if (read(fd, &comment_count, 4) < 4) { | ||
834 | return false; | ||
835 | } | ||
836 | little_endian_to_native(&comment_count, "L"); | ||
837 | packet_remaining -= (vendor_length + 8); | ||
838 | if ( packet_remaining <= 0 ) { | ||
839 | return true; | ||
840 | } | ||
841 | |||
842 | for ( i = 0; i < comment_count; i++ ) { | ||
843 | int name_length = 0; | ||
844 | |||
845 | if (read(fd, &comment_length, 4) < 4) { | ||
846 | return false; | ||
847 | } | ||
848 | |||
849 | little_endian_to_native(&comment_length, "L"); | ||
850 | |||
851 | /* Quit if we've passed the end of the page */ | ||
852 | packet_remaining -= (comment_length + 4); | ||
853 | if ( packet_remaining <= 0 ) { | ||
854 | return true; | ||
855 | } | ||
856 | |||
857 | /* Skip comment if it won't fit in buffer */ | ||
858 | if ( (unsigned int)comment_length >= sizeof(temp) ) { | ||
859 | lseek(fd, comment_length, SEEK_CUR); | ||
860 | continue; | ||
861 | } | ||
862 | |||
863 | if ( read(fd, temp, comment_length) < comment_length ) { | ||
864 | return false; | ||
865 | } | ||
866 | |||
867 | temp[comment_length] = '\0'; | ||
868 | UTF8ToAnsi(temp); | ||
869 | comment_length = strlen(temp); | ||
870 | |||
871 | if (strncasecmp(temp, "TITLE=", 6) == 0) { | ||
872 | name_length = 5; | ||
873 | p = &(entry->title); | ||
874 | } else if (strncasecmp(temp, "ALBUM=", 6) == 0) { | ||
875 | name_length = 5; | ||
876 | p = &(entry->album); | ||
877 | } else if (strncasecmp(temp, "ARTIST=", 7) == 0) { | ||
878 | name_length = 6; | ||
879 | p = &(entry->artist); | ||
880 | } else if (strncasecmp(temp, "GENRE=", 6) == 0) { | ||
881 | name_length = 5; | ||
882 | p = &(entry->genre_string); | ||
883 | } else if (strncasecmp(temp, "DATE=", 5) == 0) { | ||
884 | int j=0; | ||
885 | /* verify that this is a number */ | ||
886 | /* Note: vorbis uses UTF-8 for its comments, so it is | ||
887 | * safe to compare the values against ASCII 0 and 9 | ||
888 | */ | ||
889 | while ( j < (comment_length - 5) ) { | ||
890 | if ( (temp[5+j] < '0') || (temp[5+j] > '9') ) { | ||
891 | break; | ||
892 | } | ||
893 | j++; | ||
894 | } | ||
895 | if ( j == (comment_length - 5) ) { | ||
896 | p = NULL; | ||
897 | entry->year = atoi(temp + 5); | ||
898 | } | ||
899 | } else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) { | ||
900 | int j=0; | ||
901 | /* verify that this is a number */ | ||
902 | /* Note: vorbis uses UTF-8 for its comments, so it is | ||
903 | * safe to compare the values against ASCII 0 and 9 | ||
904 | */ | ||
905 | while ( j < (comment_length - 12) ) { | ||
906 | if ( (temp[12+j] < '0') || (temp[12+j] > '9') ) { | ||
907 | break; | ||
908 | } | ||
909 | j++; | ||
910 | } | ||
911 | if ( j == (comment_length - 12) ) { | ||
912 | p = NULL; | ||
913 | entry->tracknum = atoi(temp + 12); | ||
914 | } | ||
915 | } else { | ||
916 | p = NULL; | ||
917 | } | ||
918 | |||
919 | if (p) { | ||
920 | comment_length -= (name_length + 1); | ||
921 | if ( comment_length < buffer_remaining ) { | ||
922 | strncpy(buffer, temp + name_length + 1, comment_length); | ||
923 | buffer[comment_length] = '\0'; | ||
924 | *p = buffer; | ||
925 | buffer += comment_length + 1; | ||
926 | buffer_remaining -= comment_length + 1; | ||
927 | } | ||
928 | } | ||
929 | } | ||
930 | |||
931 | return true; | ||
932 | } | ||
933 | |||