cue.c 6.8 KB

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