diff options
Diffstat (limited to 'lib/rbcodec/metadata/ay.c')
-rw-r--r-- | lib/rbcodec/metadata/ay.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/lib/rbcodec/metadata/ay.c b/lib/rbcodec/metadata/ay.c new file mode 100644 index 0000000000..5d00264b3d --- /dev/null +++ b/lib/rbcodec/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 | static 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 | } | ||