b923ecbe |
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <string.h> |
4 | #include "cue.h" |
5 | |
6 | //#include "../PicoInt.h" |
7 | #define elprintf(w,f,...) printf(f "\n",##__VA_ARGS__); |
8 | |
9 | static char *mystrip(char *str) |
10 | { |
11 | int i, len; |
12 | |
13 | len = strlen(str); |
14 | for (i = 0; i < len; i++) |
15 | if (str[i] != ' ') break; |
16 | if (i > 0) memmove(str, str + i, len - i + 1); |
17 | |
18 | len = strlen(str); |
19 | for (i = len - 1; i >= 0; i--) |
20 | if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break; |
21 | str[i+1] = 0; |
22 | |
23 | return str; |
24 | } |
25 | |
26 | static int get_token(const char *buff, char *dest, int len) |
27 | { |
28 | const char *p = buff; |
29 | char sep = ' '; |
30 | int d = 0, skip = 0; |
31 | |
32 | while (*p && *p == ' ') { |
33 | skip++; |
34 | p++; |
35 | } |
36 | |
37 | if (*p == '\"') { |
38 | sep = '\"'; |
39 | p++; |
40 | } |
41 | while (*p && *p != sep && d < len-1) |
42 | dest[d++] = *p++; |
43 | dest[d] = 0; |
44 | |
45 | if (*p != sep) |
46 | elprintf(EL_STATUS, "cue: bad token: \"%s\"", buff); |
47 | |
48 | return d + skip; |
49 | } |
50 | |
51 | static char *get_ext(char *fname) |
52 | { |
53 | int len = strlen(fname); |
54 | return (len >= 3) ? (fname - 3) : (fname - len); |
55 | } |
56 | |
57 | |
58 | #define BEGINS(buff,str) (strncmp(buff,str,sizeof(str)-1) == 0) |
59 | |
60 | /* note: tracks[0] is not used */ |
61 | cue_data *cue_parse(const char *fname) |
62 | { |
63 | char buff[256], current_file[256], buff2[32]; |
64 | FILE *f, *tmpf; |
65 | int ret, count = 0, count_alloc = 2; |
66 | cue_data *data; |
67 | void *tmp; |
68 | |
69 | f = fopen(fname, "r"); |
70 | if (f == NULL) return NULL; |
71 | |
72 | current_file[0] = 0; |
73 | data = calloc(1, sizeof(*data) + count_alloc * sizeof(cue_track)); |
74 | |
75 | while (!feof(f)) |
76 | { |
77 | tmp = fgets(buff, sizeof(buff), f); |
78 | if (tmp == NULL) break; |
79 | |
80 | mystrip(buff); |
81 | if (buff[0] == 0) continue; |
82 | if (BEGINS(buff, "TITLE ") || BEGINS(buff, "PERFORMER ")) |
83 | continue; /* who would put those here? Ignore! */ |
84 | else if (BEGINS(buff, "FILE ")) |
85 | { |
86 | get_token(buff+5, current_file, sizeof(current_file)); |
87 | } |
88 | else if (BEGINS(buff, "TRACK ")) |
89 | { |
90 | count++; |
91 | if (count >= count_alloc) { |
92 | count_alloc *= 2; |
93 | tmp = realloc(data, sizeof(*data) + count_alloc * sizeof(cue_track)); |
94 | if (tmp == NULL) { count--; break; } |
95 | } |
96 | memset(&data->tracks[count], 0, sizeof(data->tracks[0])); |
97 | if (count == 1 || strcmp(data->tracks[1].fname, current_file) != 0) |
98 | { |
99 | data->tracks[count].fname = strdup(current_file); |
100 | if (data->tracks[count].fname == NULL) break; |
101 | |
102 | tmpf = fopen(current_file, "rb"); |
103 | if (tmpf == NULL) { |
104 | elprintf(EL_STATUS, "cue: bad/missing file: \"%s\"", current_file); |
105 | count--; break; |
106 | } |
107 | fclose(tmpf); |
108 | } |
109 | // track number |
110 | ret = get_token(buff+6, buff2, sizeof(buff2)); |
111 | if (count != atoi(buff2)) |
112 | elprintf(EL_STATUS, "cue: track index mismatch: track %i is track %i in cue", |
113 | count, atoi(buff2)); |
114 | // check type |
115 | get_token(buff+6+ret, buff2, sizeof(buff2)); |
116 | if (strcmp(buff2, "MODE1/2352")) |
117 | data->tracks[count].type = CT_BIN; |
118 | else if (strcmp(buff2, "MODE1/2048")) |
119 | data->tracks[count].type = CT_ISO; |
120 | else if (strcmp(buff2, "AUDIO")) |
121 | { |
122 | if (data->tracks[count].fname != NULL) |
123 | { |
124 | // rely on extension, not type in cue.. |
125 | char *ext = get_ext(data->tracks[count].fname); |
126 | if (strcasecmp(ext, "mp3") == 0) |
127 | data->tracks[count].type = CT_MP3; |
128 | else if (strcasecmp(ext, "wav") == 0) |
129 | data->tracks[count].type = CT_WAV; |
130 | else { |
131 | elprintf(EL_STATUS, "unhandled audio format: \"%s\"", |
132 | data->tracks[count].fname); |
133 | } |
134 | } |
135 | } |
136 | else { |
137 | elprintf(EL_STATUS, "unhandled track type: \"%s\"", buff2); |
138 | } |
139 | } |
140 | else if (BEGINS(buff, "INDEX ")) |
141 | { |
142 | int m, s, f; |
143 | // type |
144 | ret = get_token(buff+6, buff2, sizeof(buff2)); |
145 | if (atoi(buff2) == 0) continue; |
146 | if (atoi(buff2) != 1) { |
147 | elprintf(EL_STATUS, "cue: don't know how to handle: \"%s\"", buff); |
148 | count--; break; |
149 | } |
150 | // offset in file |
151 | get_token(buff+6+ret, buff2, sizeof(buff2)); |
152 | ret = sscanf(buff2, "%i:%i:%i", &m, &s, &f); |
153 | if (ret != 3) { |
154 | elprintf(EL_STATUS, "cue: failed to parse: \"%s\"", buff); |
155 | count--; break; |
156 | } |
157 | data->tracks[count].sector_offset = m*60*75 + s*75 + f; |
158 | } |
159 | else if (BEGINS(buff, "PREGAP ")) |
160 | { |
161 | int m, s, f; |
162 | get_token(buff+7, buff2, sizeof(buff2)); |
163 | ret = sscanf(buff2, "%i:%i:%i", &m, &s, &f); |
164 | if (ret != 3) { |
165 | elprintf(EL_STATUS, "cue: failed to parse: \"%s\"", buff); |
166 | continue; |
167 | } |
168 | data->tracks[count].pregap = m*60*75 + s*75 + f; |
169 | } |
170 | else |
171 | { |
172 | elprintf(EL_STATUS, "cue: failed to parse: \"%s\"", buff); |
173 | } |
174 | } |
175 | |
176 | if (count < 1 || data->tracks[1].fname == NULL) { |
177 | // failed.. |
178 | for (; count > 0; count--) |
179 | if (data->tracks[count].fname != NULL) |
180 | free(data->tracks[count].fname); |
181 | free(data); |
182 | return NULL; |
183 | } |
184 | |
185 | data->track_count = count; |
186 | return data; |
187 | } |
188 | |
189 | |
190 | void cue_destroy(cue_data *data) |
191 | { |
192 | int c; |
193 | |
194 | if (data == NULL) return; |
195 | |
196 | for (c = data->track_count; c > 0; c--) |
197 | if (data->tracks[c].fname != NULL) |
198 | free(data->tracks[c].fname); |
199 | free(data); |
200 | } |
201 | |
202 | |
203 | int main(int argc, char *argv[]) |
204 | { |
205 | cue_data *data = cue_parse(argv[1]); |
206 | int c; |
207 | |
208 | if (data == NULL) return 1; |
209 | |
210 | for (c = 1; c <= data->track_count; c++) |
211 | printf("%2i: %i %9i %9i %s\n", c, data->tracks[c].type, data->tracks[c].sector_offset, |
212 | data->tracks[c].pregap, data->tracks[c].fname); |
213 | |
214 | cue_destroy(data); |
215 | |
216 | return 0; |
217 | } |
218 | |