| 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 | #if defined(__GNUC__) && __GNUC__ >= 7 |
| 15 | #pragma GCC diagnostic ignored "-Wformat-truncation" |
| 16 | #endif |
| 17 | |
| 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 | |
| 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 |
| 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 | |
| 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 |