summaryrefslogtreecommitdiff
path: root/apps/metadata
diff options
context:
space:
mode:
Diffstat (limited to 'apps/metadata')
-rw-r--r--apps/metadata/ay.c148
-rw-r--r--apps/metadata/gbs.c65
-rw-r--r--apps/metadata/hes.c39
-rw-r--r--apps/metadata/kss.c53
-rw-r--r--apps/metadata/metadata_common.c8
-rw-r--r--apps/metadata/metadata_common.h1
-rw-r--r--apps/metadata/metadata_parsers.h6
-rw-r--r--apps/metadata/nsf.c12
-rw-r--r--apps/metadata/sgc.c67
-rw-r--r--apps/metadata/vgm.c195
10 files changed, 591 insertions, 3 deletions
diff --git a/apps/metadata/ay.c b/apps/metadata/ay.c
new file mode 100644
index 0000000000..8b737a7eba
--- /dev/null
+++ b/apps/metadata/ay.c
@@ -0,0 +1,148 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12
13/* Taken from blargg's Game_Music_Emu library */
14
15typedef unsigned char byte;
16
17/* AY file header */
18enum { header_size = 0x14 };
19struct header_t
20{
21 byte tag[8];
22 byte vers;
23 byte player;
24 byte unused[2];
25 byte author[2];
26 byte comment[2];
27 byte max_track;
28 byte first_track;
29 byte track_info[2];
30};
31
32struct file_t {
33 struct header_t const* header;
34 byte const* tracks;
35 byte const* end; /* end of file data */
36};
37
38static int get_be16( const void *a )
39{
40 return get_short_be( (void*) a );
41}
42
43/* Given pointer to 2-byte offset of data, returns pointer to data, or NULL if
44 * offset is 0 or there is less than min_size bytes of data available. */
45static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )
46{
47 int offset = (int16_t) get_be16( ptr );
48 int pos = ptr - (byte const*) file->header;
49 int size = file->end - (byte const*) file->header;
50 int limit = size - min_size;
51 if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )
52 return NULL;
53 return ptr + offset;
54}
55
56static const char *parse_header( byte const in [], int size, struct file_t* out )
57{
58 if ( size < header_size )
59 return "wrong file type";
60
61 out->header = (struct header_t const*) in;
62 out->end = in + size;
63 struct header_t const* h = (struct header_t const*) in;
64 if ( memcmp( h->tag, "ZXAYEMUL", 8 ) )
65 return "wrong file type";
66
67 out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );
68 if ( !out->tracks )
69 return "missing track data";
70
71 return 0;
72}
73
74static void copy_ay_fields( struct file_t const* file, struct mp3entry* id3, int track )
75{
76 int track_count = file->header->max_track + 1;
77
78 /* calculate track length based on number of subtracks */
79 if (track_count > 1) {
80 id3->length = file->header->max_track * 1000;
81 } else {
82 byte const* track_info = get_data( file, file->tracks + track * 4 + 2, 6 );
83 if (track_info)
84 id3->length = get_be16( track_info + 4 ) * (1000 / 50); /* frames to msec */
85 else id3->length = 120 * 1000;
86 }
87
88 if ( id3->length <= 0 )
89 id3->length = 120 * 1000; /* 2 minutes */
90
91 /* If meta info was found in the m3u skip next step */
92 if (id3->title && id3->title[0]) return;
93
94 /* If file has more than one track will
95 use file name as title */
96 char * tmp;
97 if (track_count <= 1) {
98 tmp = (char *) get_data( file, file->tracks + track * 4, 1 );
99 if ( tmp ) id3->title = tmp;
100 }
101
102 /* Author */
103 tmp = (char *) get_data( file, file->header->author, 1 );
104 if (tmp) id3->artist = tmp;
105
106 /* Comment */
107 tmp = (char *) get_data( file, file->header->comment, 1 );
108 if (tmp) id3->comment = tmp;
109}
110
111bool parse_ay_header(int fd, struct mp3entry *id3)
112{
113 /* Use the trackname part of the id3 structure as a temporary buffer */
114 unsigned char* buf = (unsigned char *)id3->id3v2buf;
115 struct file_t file;
116 int read_bytes;
117
118 lseek(fd, 0, SEEK_SET);
119 if ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) < header_size)
120 return false;
121
122 buf [ID3V2_BUF_SIZE] = '\0';
123 if ( parse_header( buf, read_bytes, &file ) )
124 return false;
125
126 copy_ay_fields( &file, id3, 0 );
127 return true;
128}
129
130bool get_ay_metadata(int fd, struct mp3entry* id3)
131{
132 char ay_type[8];
133 if ((lseek(fd, 0, SEEK_SET) < 0) ||
134 read(fd, ay_type, 8) < 8)
135 return false;
136
137 id3->vbr = false;
138 id3->filesize = filesize(fd);
139
140 id3->bitrate = 706;
141 id3->frequency = 44100;
142
143 /* Make sure this is a ZX Ay file */
144 if (memcmp( ay_type, "ZXAYEMUL", 8 ) != 0)
145 return false;
146
147 return parse_ay_header(fd, id3);
148}
diff --git a/apps/metadata/gbs.c b/apps/metadata/gbs.c
new file mode 100644
index 0000000000..796db5932f
--- /dev/null
+++ b/apps/metadata/gbs.c
@@ -0,0 +1,65 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12
13bool parse_gbs_header(int fd, struct mp3entry* id3)
14{
15 /* Use the trackname part of the id3 structure as a temporary buffer */
16 unsigned char* buf = (unsigned char *)id3->path;
17 lseek(fd, 0, SEEK_SET);
18 if (read(fd, buf, 112) < 112)
19 return false;
20
21 /* Calculate track length with number of subtracks */
22 id3->length = buf[4] * 1000;
23
24 /* If meta info was found in the m3u skip next step */
25 if (id3->title && id3->title[0]) return true;
26
27 char *p = id3->id3v2buf;
28
29 /* Some metadata entries have 32 bytes length */
30 /* Game */
31 memcpy(p, &buf[16], 32); *(p + 33) = '\0';
32 id3->title = p;
33 p += strlen(p)+1;
34
35 /* Artist */
36 memcpy(p, &buf[48], 32); *(p + 33) = '\0';
37 id3->artist = p;
38 p += strlen(p)+1;
39
40 /* Copyright */
41 memcpy(p, &buf[80], 32); *(p + 33) = '\0';
42 id3->album = p;
43
44 return true;
45}
46
47bool get_gbs_metadata(int fd, struct mp3entry* id3)
48{
49 char gbs_type[3];
50 if ((lseek(fd, 0, SEEK_SET) < 0) ||
51 (read(fd, gbs_type, 3) < 3))
52 return false;
53
54 id3->vbr = false;
55 id3->filesize = filesize(fd);
56 /* we only render 16 bits, 44.1KHz, Stereo */
57 id3->bitrate = 706;
58 id3->frequency = 44100;
59
60 /* Check for GBS magic */
61 if (memcmp( gbs_type, "GBS", 3 ) != 0)
62 return false;
63
64 return parse_gbs_header(fd, id3);
65}
diff --git a/apps/metadata/hes.c b/apps/metadata/hes.c
new file mode 100644
index 0000000000..6d99d523cb
--- /dev/null
+++ b/apps/metadata/hes.c
@@ -0,0 +1,39 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12#include "plugin.h"
13
14bool get_hes_metadata(int fd, struct mp3entry* id3)
15{
16 /* Use the id3v2 buffer part of the id3 structure as a temporary buffer */
17 unsigned char* buf = (unsigned char *)id3->id3v2buf;
18 int read_bytes;
19
20 if ((lseek(fd, 0, SEEK_SET) < 0)
21 || ((read_bytes = read(fd, buf, 4)) < 4))
22 return false;
23
24 /* Verify this is a HES file */
25 if (memcmp(buf,"HESM",4) != 0)
26 return false;
27
28 id3->vbr = false;
29 id3->filesize = filesize(fd);
30 /* we only render 16 bits, 44.1KHz, Stereo */
31 id3->bitrate = 706;
32 id3->frequency = 44100;
33
34 /* Set default track count (length)*/
35 id3->length = 255 * 1000;
36
37 return true;
38}
39
diff --git a/apps/metadata/kss.c b/apps/metadata/kss.c
new file mode 100644
index 0000000000..ecb59b8353
--- /dev/null
+++ b/apps/metadata/kss.c
@@ -0,0 +1,53 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12
13bool parse_kss_header(int fd, struct mp3entry* id3)
14{
15 /* Use the trackname part of the id3 structure as a temporary buffer */
16 unsigned char* buf = (unsigned char *)id3->path;
17
18 lseek(fd, 0, SEEK_SET);
19 if (read(fd, buf, 0x20) < 0x20)
20 return false;
21
22 /* calculate track length with number of tracks */
23 id3->length = 0;
24 if (buf[14] == 0x10) {
25 id3->length = (get_short_le((void *)(buf + 26)) + 1) * 1000;
26 }
27
28 if (id3->length <= 0)
29 id3->length = 255 * 1000; /* 255 tracks */
30
31 return true;
32}
33
34
35bool get_kss_metadata(int fd, struct mp3entry* id3)
36{
37 uint32_t kss_type;
38 if ((lseek(fd, 0, SEEK_SET) < 0) ||
39 read_uint32be(fd, &kss_type) != (int)sizeof(kss_type))
40 return false;
41
42 id3->vbr = false;
43 id3->filesize = filesize(fd);
44 /* we only render 16 bits, 44.1KHz, Stereo */
45 id3->bitrate = 706;
46 id3->frequency = 44100;
47
48 /* Make sure this is an SGC file */
49 if (kss_type != FOURCC('K','S','C','C') && kss_type != FOURCC('K','S','S','X'))
50 return false;
51
52 return parse_kss_header(fd, id3);
53}
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c
index 6c420d921f..1ad89d1b7a 100644
--- a/apps/metadata/metadata_common.c
+++ b/apps/metadata/metadata_common.c
@@ -178,6 +178,14 @@ unsigned long get_long_be(void* buf)
178 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 178 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
179} 179}
180 180
181/* Read an unaligned 16-bit little endian short from buffer. */
182unsigned short get_short_be(void* buf)
183{
184 unsigned char* p = (unsigned char*) buf;
185
186 return (p[0] << 8) | p[1];
187}
188
181/* Read an unaligned 32-bit little endian long from buffer. */ 189/* Read an unaligned 32-bit little endian long from buffer. */
182long get_slong(void* buf) 190long get_slong(void* buf)
183{ 191{
diff --git a/apps/metadata/metadata_common.h b/apps/metadata/metadata_common.h
index b2c76afb45..a48c2a4e89 100644
--- a/apps/metadata/metadata_common.h
+++ b/apps/metadata/metadata_common.h
@@ -62,6 +62,7 @@ uint64_t get_uint64_le(void* buf);
62unsigned long get_long_le(void* buf); 62unsigned long get_long_le(void* buf);
63unsigned short get_short_le(void* buf); 63unsigned short get_short_le(void* buf);
64unsigned long get_long_be(void* buf); 64unsigned long get_long_be(void* buf);
65unsigned short get_short_be(void* buf);
65long get_slong(void* buf); 66long get_slong(void* buf);
66unsigned long get_itunes_int32(char* value, int count); 67unsigned long get_itunes_int32(char* value, int count);
67long parse_tag(const char* name, char* value, struct mp3entry* id3, 68long parse_tag(const char* name, char* value, struct mp3entry* id3,
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h
index 7797b47094..adb7a82cd5 100644
--- a/apps/metadata/metadata_parsers.h
+++ b/apps/metadata/metadata_parsers.h
@@ -47,3 +47,9 @@ bool get_au_metadata(int fd, struct mp3entry* id3);
47bool get_vox_metadata(int fd, struct mp3entry* id3); 47bool get_vox_metadata(int fd, struct mp3entry* id3);
48bool get_wave64_metadata(int fd, struct mp3entry* id3); 48bool get_wave64_metadata(int fd, struct mp3entry* id3);
49bool get_tta_metadata(int fd, struct mp3entry* id3); 49bool get_tta_metadata(int fd, struct mp3entry* id3);
50bool get_ay_metadata(int fd, struct mp3entry* id3);
51bool get_gbs_metadata(int fd, struct mp3entry* id3);
52bool get_hes_metadata(int fd, struct mp3entry* id3);
53bool get_sgc_metadata(int fd, struct mp3entry* id3);
54bool get_vgm_metadata(int fd, struct mp3entry* id3);
55bool get_kss_metadata(int fd, struct mp3entry* id3);
diff --git a/apps/metadata/nsf.c b/apps/metadata/nsf.c
index 29fd8475bb..2fa6f36b12 100644
--- a/apps/metadata/nsf.c
+++ b/apps/metadata/nsf.c
@@ -11,6 +11,9 @@
11#include "rbunicode.h" 11#include "rbunicode.h"
12#include "string-extra.h" 12#include "string-extra.h"
13 13
14/* NOTE: This file was modified to work properly with the new nsf codec based
15 on Game_Music_Emu */
16
14struct NESM_HEADER 17struct NESM_HEADER
15{ 18{
16 uint32_t nHeader; 19 uint32_t nHeader;
@@ -66,7 +69,7 @@ static bool parse_nsfe(int fd, struct mp3entry *id3)
66 69
67 /* default values */ 70 /* default values */
68 info.nTrackCount = 1; 71 info.nTrackCount = 1;
69 id3->length = 2*1000*60; 72 id3->length = 150 * 1000;
70 73
71 /* begin reading chunks */ 74 /* begin reading chunks */
72 while (!(chunks_found & CHUNK_NEND)) 75 while (!(chunks_found & CHUNK_NEND))
@@ -210,6 +213,10 @@ static bool parse_nsfe(int fd, struct mp3entry *id3)
210 if (track_count | playlist_count) 213 if (track_count | playlist_count)
211 id3->length = MAX(track_count, playlist_count)*1000; 214 id3->length = MAX(track_count, playlist_count)*1000;
212 215
216 /* Single subtrack files will be treated differently
217 by gme's nsf codec */
218 if (id3->length <= 1000) id3->length = 150 * 1000;
219
213 /* 220 /*
214 * if we exited the while loop without a 'return', we must have hit an NEND 221 * if we exited the while loop without a 'return', we must have hit an NEND
215 * chunk if this is the case, the file was layed out as it was expected. 222 * chunk if this is the case, the file was layed out as it was expected.
@@ -230,7 +237,7 @@ static bool parse_nesm(int fd, struct mp3entry *id3)
230 return false; 237 return false;
231 238
232 /* Length */ 239 /* Length */
233 id3->length = hdr.nTrackCount*1000; 240 id3->length = (hdr.nTrackCount > 1 ? hdr.nTrackCount : 150) * 1000;
234 241
235 /* Title */ 242 /* Title */
236 id3->title = p; 243 id3->title = p;
@@ -250,7 +257,6 @@ static bool parse_nesm(int fd, struct mp3entry *id3)
250bool get_nsf_metadata(int fd, struct mp3entry* id3) 257bool get_nsf_metadata(int fd, struct mp3entry* id3)
251{ 258{
252 uint32_t nsf_type; 259 uint32_t nsf_type;
253
254 if (lseek(fd, 0, SEEK_SET) < 0 || 260 if (lseek(fd, 0, SEEK_SET) < 0 ||
255 read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type)) 261 read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type))
256 return false; 262 return false;
diff --git a/apps/metadata/sgc.c b/apps/metadata/sgc.c
new file mode 100644
index 0000000000..9e16de2c76
--- /dev/null
+++ b/apps/metadata/sgc.c
@@ -0,0 +1,67 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12
13bool parse_sgc_header(int fd, struct mp3entry* id3)
14{
15 /* Use the trackname part of the id3 structure as a temporary buffer */
16 unsigned char* buf = (unsigned char *)id3->path;
17
18 lseek(fd, 0, SEEK_SET);
19 if (read(fd, buf, 0xA0) < 0xA0)
20 return false;
21
22 /* calculate track length with number of tracks */
23 id3->length = buf[37] * 1000;
24
25 /* If meta info was found in the m3u skip next step */
26 if (id3->title && id3->title[0]) return true;
27
28 char *p = id3->id3v2buf;
29
30 /* Some metadata entries have 32 bytes length */
31 /* Game */
32 memcpy(p, &buf[64], 32); *(p + 33) = '\0';
33 id3->title = p;
34 p += strlen(p)+1;
35
36 /* Artist */
37 memcpy(p, &buf[96], 32); *(p + 33) = '\0';
38 id3->artist = p;
39 p += strlen(p)+1;
40
41 /* Copyright */
42 memcpy(p, &buf[128], 32); *(p + 33) = '\0';
43 id3->album = p;
44 p += strlen(p)+1;
45 return true;
46}
47
48
49bool get_sgc_metadata(int fd, struct mp3entry* id3)
50{
51 uint32_t sgc_type;
52 if ((lseek(fd, 0, SEEK_SET) < 0) ||
53 read_uint32be(fd, &sgc_type) != (int)sizeof(sgc_type))
54 return false;
55
56 id3->vbr = false;
57 id3->filesize = filesize(fd);
58 /* we only render 16 bits, 44.1KHz, Stereo */
59 id3->bitrate = 706;
60 id3->frequency = 44100;
61
62 /* Make sure this is an SGC file */
63 if (sgc_type != FOURCC('S','G','C',0x1A))
64 return false;
65
66 return parse_sgc_header(fd, id3);
67}
diff --git a/apps/metadata/vgm.c b/apps/metadata/vgm.c
new file mode 100644
index 0000000000..9ea95b3939
--- /dev/null
+++ b/apps/metadata/vgm.c
@@ -0,0 +1,195 @@
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <ctype.h>
5#include <inttypes.h>
6
7#include "system.h"
8#include "metadata.h"
9#include "metadata_common.h"
10#include "metadata_parsers.h"
11#include "rbunicode.h"
12
13/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
14
15typedef unsigned char byte;
16
17enum { header_size = 0x40 };
18enum { max_field = 64 };
19
20struct header_t
21{
22 char tag [4];
23 byte data_size [4];
24 byte version [4];
25 byte psg_rate [4];
26 byte ym2413_rate [4];
27 byte gd3_offset [4];
28 byte track_duration [4];
29 byte loop_offset [4];
30 byte loop_duration [4];
31 byte frame_rate [4];
32 byte noise_feedback [2];
33 byte noise_width;
34 byte unused1;
35 byte ym2612_rate [4];
36 byte ym2151_rate [4];
37 byte data_offset [4];
38 byte unused2 [8];
39};
40
41static byte const* skip_gd3_str( byte const* in, byte const* end )
42{
43 while ( end - in >= 2 )
44 {
45 in += 2;
46 if ( !(in [-2] | in [-1]) )
47 break;
48 }
49 return in;
50}
51
52static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
53{
54 byte const* mid = skip_gd3_str( in, end );
55 int len = (mid - in) / 2 - 1;
56 if ( field && len > 0 )
57 {
58 len = len < (int) max_field ? len : (int) max_field;
59
60 field [len] = 0;
61 /* Conver to utf8 */
62 utf16LEdecode( in, field, len );
63
64 /* Copy string back to id3v2buf */
65 strcpy( (char*) in, field );
66 }
67 return mid;
68}
69
70static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
71{
72 return skip_gd3_str( get_gd3_str( in, end, field ), end );
73}
74
75static void parse_gd3( byte const* in, byte const* end, struct mp3entry* id3 )
76{
77 char* p = id3->path;
78 id3->title = (char *) in;
79 in = get_gd3_pair( in, end, p ); /* Song */
80
81 id3->album = (char *) in;
82 in = get_gd3_pair( in, end, p ); /* Game */
83
84 in = get_gd3_pair( in, end, NULL ); /* System */
85
86 id3->artist = (char *) in;
87 in = get_gd3_pair( in, end, p ); /* Author */
88
89#if MEMORYSIZE > 2
90 in = get_gd3_str ( in, end, NULL ); /* Copyright */
91 in = get_gd3_pair( in, end, NULL ); /* Dumper */
92
93 id3->comment = (char *) in;
94 in = get_gd3_str ( in, end, p ); /* Comment */
95#endif
96}
97
98int const gd3_header_size = 12;
99
100static long check_gd3_header( byte* h, long remain )
101{
102 if ( remain < gd3_header_size ) return 0;
103 if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
104 if ( get_long_le( h + 4 ) >= 0x200 ) return 0;
105
106 long gd3_size = get_long_le( h + 8 );
107 if ( gd3_size > remain - gd3_header_size )
108 gd3_size = remain - gd3_header_size;
109
110 return gd3_size;
111}
112
113static void get_vgm_length( struct header_t* h, struct mp3entry* id3 )
114{
115 long length = get_long_le( h->track_duration ) * 10 / 441;
116 if ( length > 0 )
117 {
118 long loop_length = 0, intro_length = 0;
119 long loop = get_long_le( h->loop_duration );
120 if ( loop > 0 && get_long_le( h->loop_offset ) )
121 {
122 loop_length = loop * 10 / 441;
123 intro_length = length - loop_length;
124 }
125 else
126 {
127 intro_length = length; /* make it clear that track is no longer than length */
128 loop_length = 0;
129 }
130
131 id3->length = intro_length + 2 * loop_length; /* intro + 2 loops */
132 return;
133 }
134
135 id3->length = 150 * 1000; /* 2.5 minutes */
136}
137
138bool get_vgm_metadata(int fd, struct mp3entry* id3)
139{
140 /* Use the id3v2 part of the id3 structure as a temporary buffer */
141 unsigned char* buf = (unsigned char *)id3->id3v2buf;
142 int read_bytes;
143
144 memset(buf, 0, ID3V2_BUF_SIZE);
145 if ((lseek(fd, 0, SEEK_SET) < 0)
146 || ((read_bytes = read(fd, buf, header_size)) < header_size))
147 {
148 return false;
149 }
150
151 id3->vbr = false;
152 id3->filesize = filesize(fd);
153
154 id3->bitrate = 706;
155 id3->frequency = 44100;
156
157 /* If file is gzipped, will get metadata later */
158 if (memcmp(buf, "Vgm ", 4))
159 {
160 /* We must set a default song length here because
161 the codec can't do it anymore */
162 id3->length = 150 * 1000; /* 2.5 minutes */
163 return true;
164 }
165
166 /* Get song length from header */
167 struct header_t* header = (struct header_t*) buf;
168 get_vgm_length( header, id3 );
169
170 long gd3_offset = get_long_le( header->gd3_offset ) - 0x2C;
171
172 /* No gd3 tag found */
173 if ( gd3_offset < 0 )
174 return true;
175
176 /* Seek to gd3 offset and read as
177 many bytes posible */
178 gd3_offset = id3->filesize - (header_size + gd3_offset);
179 if ((lseek(fd, -gd3_offset, SEEK_END) < 0)
180 || ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) <= 0))
181 return true;
182
183 byte* gd3 = buf;
184 long gd3_size = check_gd3_header( gd3, read_bytes );
185
186 /* GD3 tag is zero */
187 if ( gd3_size == 0 )
188 return true;
189
190 /* Finally, parse gd3 tag */
191 if ( gd3 )
192 parse_gd3( gd3 + gd3_header_size, gd3 + read_bytes, id3 );
193
194 return true;
195}