1 /*
2  * CD image handler
3  * (C) notaz, 2007,2013
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8 
9 #include "../pico_int.h"
10 #include "genplus_macros.h"
11 #include "cdd.h"
12 #include "cue.h"
13 
14 #ifdef USE_LIBRETRO_VFS
15 #include "file_stream_transforms.h"
16 #endif
17 
handle_mp3(const char * fname,int index)18 static int handle_mp3(const char *fname, int index)
19 {
20   track_t *track = &cdd.toc.tracks[index];
21   FILE *tmp_file;
22   int kBps;
23   int fs, ret;
24 
25   tmp_file = fopen(fname, "rb");
26   if (tmp_file == NULL)
27     return -1;
28 
29   ret = fseek(tmp_file, 0, SEEK_END);
30   fs = ftell(tmp_file);
31   fseek(tmp_file, 0, SEEK_SET);
32 
33 #ifdef _PSP_FW_VERSION
34   // some systems (like PSP) can't have many open files at a time,
35   // so we work with their names instead.
36   fclose(tmp_file);
37   tmp_file = (void *) strdup(fname);
38 #endif
39 
40   kBps = mp3_get_bitrate(tmp_file, fs) / 8;
41   if (ret != 0 || kBps <= 0)
42   {
43     elprintf(EL_STATUS, "track %2i: mp3 bitrate %i", index+1, kBps);
44 #ifdef _PSP_FW_VERSION
45     free(tmp_file);
46 #else
47     fclose(tmp_file);
48 #endif
49     return -1;
50   }
51 
52   track->fd = tmp_file;
53   track->offset = 0;
54 
55   fs *= 75;
56   fs /= kBps * 1000;
57   return fs;
58 }
59 
to_upper(char * d,const char * s)60 static void to_upper(char *d, const char *s)
61 {
62   for (; *s != 0; d++, s++) {
63     if ('a' <= *s && *s <= 'z')
64       *d = *s - 'a' + 'A';
65     else
66       *d = *s;
67   }
68   *d = 0;
69 }
70 
71 // cdd.c uses lba - 150
sprintf_lba(char * buf,size_t size,int lba)72 static void sprintf_lba(char *buf, size_t size, int lba)
73 {
74   lba += 150;
75   snprintf(buf, size, "%02d:%02d:%02d", lba / 60 / 75,
76     (lba / 75) % 60, lba % 75);
77 }
78 
load_cd_image(const char * cd_img_name,int * type)79 int load_cd_image(const char *cd_img_name, int *type)
80 {
81   static const char *exts[] = {
82     "%02d.mp3", " %02d.mp3", "-%02d.mp3", "_%02d.mp3", " - %02d.mp3",
83     "%d.mp3", " %d.mp3", "-%d.mp3", "_%d.mp3", " - %d.mp3",
84   };
85   int i, j, n, lba, index, length, ret;
86   int iso_name_len, missed, cd_img_sectors;
87   char tmp_name[256], tmp_ext[10], tmp_ext_u[10];
88   track_t *tracks = cdd.toc.tracks;
89   cue_data_t *cue_data = NULL;
90   pm_file *pmf;
91 
92   if (PicoCDLoadProgressCB != NULL)
93     PicoCDLoadProgressCB(cd_img_name, 1);
94 
95   Pico_mcd->cdda_type = CT_UNKNOWN;
96 
97   /* is this a .cue? */
98   cue_data = cue_parse(cd_img_name);
99   if (cue_data != NULL) {
100     cd_img_name = cue_data->tracks[1].fname;
101     *type = cue_data->tracks[1].type;
102   }
103 
104   pmf = pm_open(cd_img_name);
105   if (pmf == NULL)
106   {
107     if (cue_data != NULL)
108       cue_destroy(cue_data);
109     return -1;
110   }
111   tracks[0].fd = pmf;
112 
113   if (*type == CT_ISO)
114        cd_img_sectors = pmf->size >>= 11;  // size in sectors
115   else cd_img_sectors = pmf->size /= 2352;
116 
117   // cdd.c operates with lba - 150
118   tracks[0].start = 0;
119   tracks[0].end = cd_img_sectors;
120   tracks[0].offset = 0;
121 
122   sprintf_lba(tmp_ext, sizeof(tmp_ext), 0);
123   elprintf(EL_STATUS, "Track  1: %s %9i DATA  %s",
124     tmp_ext, tracks[0].end, cd_img_name);
125 
126   lba = cd_img_sectors;
127 
128   if (cue_data != NULL)
129   {
130     if (cue_data->tracks[2].fname == NULL) {
131       // NULL fname means track2 is in same file as track1
132       lba = tracks[0].end = cue_data->tracks[2].sector_offset;
133     }
134     i = 100 / cue_data->track_count + 1; // progress display
135 
136     for (n = 2; n <= cue_data->track_count; n++)
137     {
138       if (PicoCDLoadProgressCB != NULL)
139         PicoCDLoadProgressCB(cd_img_name, i * n);
140 
141       index = n - 1;
142       lba += cue_data->tracks[n].pregap;
143       if (cue_data->tracks[n].type == CT_MP3) {
144         ret = handle_mp3(cue_data->tracks[n].fname, index);
145         if (ret < 0)
146           break;
147         length = ret;
148       }
149       else if (cue_data->tracks[n].fname != NULL)
150       {
151         pm_file *f = pm_open(cue_data->tracks[n].fname);
152         if (f != NULL)
153         {
154           // assume raw, ignore header for wav..
155           tracks[index].fd = f;
156           tracks[index].offset = cue_data->tracks[n].sector_offset;
157           length = f->size / 2352;
158         }
159         else
160         {
161           elprintf(EL_STATUS, "track %2i (%s): can't determine length",
162             n, cue_data->tracks[n].fname);
163           tracks[index].offset = 0;
164           length = 2*75;
165         }
166       }
167       else
168       {
169         if (n < cue_data->track_count)
170           length = cue_data->tracks[n+1].sector_offset -
171             cue_data->tracks[n].sector_offset;
172         else
173           length = cd_img_sectors - cue_data->tracks[n].sector_offset;
174         tracks[index].offset = cue_data->tracks[n].sector_offset;
175       }
176 
177       if (cue_data->tracks[n].sector_xlength != 0)
178         // overriden by custom cue command
179         length = cue_data->tracks[n].sector_xlength;
180 
181       Pico_mcd->cdda_type = cue_data->tracks[n].type;
182 
183       tracks[index].start = lba;
184       lba += length;
185       tracks[index].end = lba;
186 
187       sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start);
188       elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO %s",
189         n, tmp_ext, length, cue_data->tracks[n].fname);
190     }
191     goto finish;
192   }
193 
194   /* mp3 track autosearch, Gens-like */
195   iso_name_len = strlen(cd_img_name);
196   if (iso_name_len >= sizeof(tmp_name))
197     iso_name_len = sizeof(tmp_name) - 1;
198 
199   for (n = 2, i = 0, missed = 0; i < 100 && missed < 4; i++)
200   {
201     if (PicoCDLoadProgressCB != NULL && i > 1)
202       PicoCDLoadProgressCB(cd_img_name, i + (100-i)*missed/4);
203 
204     for (j = 0; j < sizeof(exts)/sizeof(char *); j++)
205     {
206       int ext_len;
207       char *p;
208 
209       index = n - 1;
210 
211       snprintf(tmp_ext, sizeof(tmp_ext), exts[j], i);
212       ext_len = strlen(tmp_ext);
213       to_upper(tmp_ext_u, tmp_ext);
214 
215       memcpy(tmp_name, cd_img_name, iso_name_len + 1);
216       p = tmp_name + iso_name_len - 4;
217 
218       strcpy(p, tmp_ext);
219       ret = handle_mp3(tmp_name, index);
220       if (ret <= 0) {
221         strcpy(p, tmp_ext_u);
222         ret = handle_mp3(tmp_name, index);
223       }
224 
225       if (ret <= 0 && i > 1 && iso_name_len > ext_len) {
226         p = tmp_name + iso_name_len - ext_len;
227         strcpy(p, tmp_ext);
228         ret = handle_mp3(tmp_name, index);
229         if (ret <= 0) {
230           strcpy(p, tmp_ext_u);
231           ret = handle_mp3(tmp_name, index);
232         }
233       }
234 
235       if (ret > 0)
236       {
237         length = ret;
238         tracks[index].start = lba;
239         lba += length;
240         tracks[index].end = lba;
241 
242         Pico_mcd->cdda_type = CT_MP3;
243 
244         sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start);
245         elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO - %s",
246           n, tmp_ext, length, tmp_name);
247 
248         n++;
249         missed = 0;
250         break;
251       }
252     }
253     if (ret <= 0 && i > 1)
254       missed++;
255   }
256 
257 finish:
258   cdd.toc.last = n - 1;
259   cdd.toc.end = lba;
260 
261   sprintf_lba(tmp_ext, sizeof(tmp_ext), cdd.toc.end);
262   elprintf(EL_STATUS, "End CD -  %s\n", tmp_ext);
263 
264   if (PicoCDLoadProgressCB != NULL)
265     PicoCDLoadProgressCB(cd_img_name, 100);
266 
267   if (cue_data != NULL)
268     cue_destroy(cue_data);
269 
270   return 0;
271 }
272 
273 // vim:shiftwidth=2:ts=2:expandtab
274