diff options
author | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
---|---|---|
committer | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
commit | acb0917556fc33681c1df5a530cf754193e67705 (patch) | |
tree | 052a47097009a210e4aed9c207bd6aa4828cc000 /apps/metadata | |
parent | 93c6f1329a5691a8be158cefe15641bd1daf9ef8 (diff) | |
download | rockbox-acb0917556fc33681c1df5a530cf754193e67705.tar.gz rockbox-acb0917556fc33681c1df5a530cf754193e67705.zip |
Submit initial patch from FS#12176. Adds support for several new game music formats (AY, GBS, HES, KSS, SGC, VGM and VGZ) and replaces the current NSF and NSFE with a new implementation based on a port of the Game Music Emu library 'GME'. This first submit does not cover the full functionality provided by the author's original patch: Coleco-SGV is not supported, some GME-specific m3u-support has been removed and IRAM is not used yet. Further changes are very likely to follow this submit. Thanks to Mauricio Garrido.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30264 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/metadata')
-rw-r--r-- | apps/metadata/ay.c | 148 | ||||
-rw-r--r-- | apps/metadata/gbs.c | 65 | ||||
-rw-r--r-- | apps/metadata/hes.c | 39 | ||||
-rw-r--r-- | apps/metadata/kss.c | 53 | ||||
-rw-r--r-- | apps/metadata/metadata_common.c | 8 | ||||
-rw-r--r-- | apps/metadata/metadata_common.h | 1 | ||||
-rw-r--r-- | apps/metadata/metadata_parsers.h | 6 | ||||
-rw-r--r-- | apps/metadata/nsf.c | 12 | ||||
-rw-r--r-- | apps/metadata/sgc.c | 67 | ||||
-rw-r--r-- | apps/metadata/vgm.c | 195 |
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 | |||
15 | typedef unsigned char byte; | ||
16 | |||
17 | /* AY file header */ | ||
18 | enum { header_size = 0x14 }; | ||
19 | struct 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 | |||
32 | struct file_t { | ||
33 | struct header_t const* header; | ||
34 | byte const* tracks; | ||
35 | byte const* end; /* end of file data */ | ||
36 | }; | ||
37 | |||
38 | static 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. */ | ||
45 | static 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 | |||
56 | static 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 | |||
74 | static 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 | |||
111 | bool 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 | |||
130 | bool 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 | |||
13 | bool 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 | |||
47 | bool 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 | |||
14 | bool 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 | |||
13 | bool 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 | |||
35 | bool 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. */ | ||
182 | unsigned 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. */ |
182 | long get_slong(void* buf) | 190 | long 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); | |||
62 | unsigned long get_long_le(void* buf); | 62 | unsigned long get_long_le(void* buf); |
63 | unsigned short get_short_le(void* buf); | 63 | unsigned short get_short_le(void* buf); |
64 | unsigned long get_long_be(void* buf); | 64 | unsigned long get_long_be(void* buf); |
65 | unsigned short get_short_be(void* buf); | ||
65 | long get_slong(void* buf); | 66 | long get_slong(void* buf); |
66 | unsigned long get_itunes_int32(char* value, int count); | 67 | unsigned long get_itunes_int32(char* value, int count); |
67 | long parse_tag(const char* name, char* value, struct mp3entry* id3, | 68 | long 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); | |||
47 | bool get_vox_metadata(int fd, struct mp3entry* id3); | 47 | bool get_vox_metadata(int fd, struct mp3entry* id3); |
48 | bool get_wave64_metadata(int fd, struct mp3entry* id3); | 48 | bool get_wave64_metadata(int fd, struct mp3entry* id3); |
49 | bool get_tta_metadata(int fd, struct mp3entry* id3); | 49 | bool get_tta_metadata(int fd, struct mp3entry* id3); |
50 | bool get_ay_metadata(int fd, struct mp3entry* id3); | ||
51 | bool get_gbs_metadata(int fd, struct mp3entry* id3); | ||
52 | bool get_hes_metadata(int fd, struct mp3entry* id3); | ||
53 | bool get_sgc_metadata(int fd, struct mp3entry* id3); | ||
54 | bool get_vgm_metadata(int fd, struct mp3entry* id3); | ||
55 | bool 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 | |||
14 | struct NESM_HEADER | 17 | struct 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) | |||
250 | bool get_nsf_metadata(int fd, struct mp3entry* id3) | 257 | bool 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 | |||
13 | bool 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 | |||
49 | bool 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 | |||
15 | typedef unsigned char byte; | ||
16 | |||
17 | enum { header_size = 0x40 }; | ||
18 | enum { max_field = 64 }; | ||
19 | |||
20 | struct 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 | |||
41 | static 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 | |||
52 | static 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 | |||
70 | static 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 | |||
75 | static 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 | |||
98 | int const gd3_header_size = 12; | ||
99 | |||
100 | static 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 | |||
113 | static 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 | |||
138 | bool 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 | } | ||