| | 1 | /*************************************************************************** |
| | 2 | * Copyright (C) 2007 PCSX-df Team * |
| | 3 | * Copyright (C) 2009 Wei Mingzhi * |
| | 4 | * Copyright (C) 2012 notaz * |
| | 5 | * * |
| | 6 | * This program is free software; you can redistribute it and/or modify * |
| | 7 | * it under the terms of the GNU General Public License as published by * |
| | 8 | * the Free Software Foundation; either version 2 of the License, or * |
| | 9 | * (at your option) any later version. * |
| | 10 | * * |
| | 11 | * This program is distributed in the hope that it will be useful, * |
| | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| | 14 | * GNU General Public License for more details. * |
| | 15 | * * |
| | 16 | * You should have received a copy of the GNU General Public License * |
| | 17 | * along with this program; if not, write to the * |
| | 18 | * Free Software Foundation, Inc., * |
| | 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * |
| | 20 | ***************************************************************************/ |
| | 21 | |
| | 22 | #include "psxcommon.h" |
| | 23 | #include "cdrom.h" |
| | 24 | #include "cdriso.h" |
| | 25 | #include "ppf.h" |
| | 26 | |
| | 27 | #include <assert.h> |
| | 28 | #include <errno.h> |
| | 29 | #include <zlib.h> |
| | 30 | #ifdef HAVE_CHD |
| | 31 | #include <libchdr/chd.h> |
| | 32 | #endif |
| | 33 | |
| | 34 | #ifdef _WIN32 |
| | 35 | #define strcasecmp _stricmp |
| | 36 | #else |
| | 37 | #include <sys/types.h> |
| | 38 | #include <sys/stat.h> |
| | 39 | #include <unistd.h> |
| | 40 | #endif |
| | 41 | |
| | 42 | #ifdef USE_LIBRETRO_VFS |
| | 43 | #include <streams/file_stream_transforms.h> |
| | 44 | #undef fseeko |
| | 45 | #undef ftello |
| | 46 | #undef rewind |
| | 47 | #define ftello rftell |
| | 48 | #define fseeko rfseek |
| | 49 | #define rewind(f_) rfseek(f_, 0, SEEK_SET) |
| | 50 | #endif |
| | 51 | |
| | 52 | #define OFF_T_MSB ((off_t)1 << (sizeof(off_t) * 8 - 1)) |
| | 53 | |
| | 54 | unsigned int cdrIsoMultidiskCount; |
| | 55 | unsigned int cdrIsoMultidiskSelect; |
| | 56 | |
| | 57 | static FILE *cdHandle = NULL; |
| | 58 | static FILE *subHandle = NULL; |
| | 59 | |
| | 60 | static boolean subChanMixed = FALSE; |
| | 61 | static boolean subChanRaw = FALSE; |
| | 62 | |
| | 63 | static boolean multifile = FALSE; |
| | 64 | |
| | 65 | static unsigned char cdbuffer[CD_FRAMESIZE_RAW]; |
| | 66 | |
| | 67 | static boolean cddaBigEndian = FALSE; |
| | 68 | |
| | 69 | // compressed image stuff |
| | 70 | static struct { |
| | 71 | unsigned char buff_raw[16][CD_FRAMESIZE_RAW]; |
| | 72 | unsigned char buff_compressed[CD_FRAMESIZE_RAW * 16 + 100]; |
| | 73 | off_t *index_table; |
| | 74 | unsigned int index_len; |
| | 75 | unsigned int block_shift; |
| | 76 | unsigned int current_block; |
| | 77 | unsigned int sector_in_blk; |
| | 78 | } *compr_img; |
| | 79 | |
| | 80 | #ifdef HAVE_CHD |
| | 81 | static struct { |
| | 82 | unsigned char *buffer; |
| | 83 | chd_file* chd; |
| | 84 | const chd_header* header; |
| | 85 | unsigned int sectors_per_hunk; |
| | 86 | unsigned int current_hunk[2]; |
| | 87 | unsigned int current_buffer; |
| | 88 | unsigned int sector_in_hunk; |
| | 89 | } *chd_img; |
| | 90 | #else |
| | 91 | #define chd_img 0 |
| | 92 | #endif |
| | 93 | |
| | 94 | static int (*cdimg_read_func)(FILE *f, unsigned int base, void *dest, int sector); |
| | 95 | static int (*cdimg_read_sub_func)(FILE *f, int sector, void *dest); |
| | 96 | |
| | 97 | static void DecodeRawSubData(unsigned char *subbuffer); |
| | 98 | |
| | 99 | struct trackinfo { |
| | 100 | enum cdrType type; |
| | 101 | unsigned int start; // sector index in toc |
| | 102 | unsigned int length; // readable sectors, excludes gaps |
| | 103 | unsigned int start_offset; // byte offset from start of file (chd: sector offset) |
| | 104 | FILE *handle; // for multi-track images |
| | 105 | }; |
| | 106 | |
| | 107 | #define MAXTRACKS 100 /* How many tracks can a CD hold? */ |
| | 108 | |
| | 109 | // 1-based array (indexed [1..numtracks]) |
| | 110 | static int numtracks = 0; |
| | 111 | static struct trackinfo ti[MAXTRACKS]; |
| | 112 | |
| | 113 | // get a sector from a msf-array |
| | 114 | static unsigned int msf2sec(const void *msf_) { |
| | 115 | const unsigned char *msf = msf_; |
| | 116 | return ((msf[0] * 60 + msf[1]) * 75) + msf[2]; |
| | 117 | } |
| | 118 | |
| | 119 | static void sec2msf(unsigned int s, void *msf_) { |
| | 120 | unsigned char *msf = msf_; |
| | 121 | msf[0] = s / 75 / 60; |
| | 122 | s = s - msf[0] * 75 * 60; |
| | 123 | msf[1] = s / 75; |
| | 124 | s = s - msf[1] * 75; |
| | 125 | msf[2] = s; |
| | 126 | } |
| | 127 | |
| | 128 | // divide a string of xx:yy:zz into m, s, f |
| | 129 | static void tok2msf(char *time, char *msf) { |
| | 130 | char *token; |
| | 131 | |
| | 132 | token = strtok(time, ":"); |
| | 133 | if (token) { |
| | 134 | msf[0] = atoi(token); |
| | 135 | } |
| | 136 | else { |
| | 137 | msf[0] = 0; |
| | 138 | } |
| | 139 | |
| | 140 | token = strtok(NULL, ":"); |
| | 141 | if (token) { |
| | 142 | msf[1] = atoi(token); |
| | 143 | } |
| | 144 | else { |
| | 145 | msf[1] = 0; |
| | 146 | } |
| | 147 | |
| | 148 | token = strtok(NULL, ":"); |
| | 149 | if (token) { |
| | 150 | msf[2] = atoi(token); |
| | 151 | } |
| | 152 | else { |
| | 153 | msf[2] = 0; |
| | 154 | } |
| | 155 | } |
| | 156 | |
| | 157 | static off_t get_size(FILE *f) |
| | 158 | { |
| | 159 | off_t old, size; |
| | 160 | assert(f); |
| | 161 | #if !defined(USE_LIBRETRO_VFS) |
| | 162 | struct stat st; |
| | 163 | if (fstat(fileno(f), &st) == 0) |
| | 164 | return st.st_size; |
| | 165 | #endif |
| | 166 | old = ftello(f); |
| | 167 | fseeko(f, 0, SEEK_END); |
| | 168 | size = ftello(f); |
| | 169 | fseeko(f, old, SEEK_SET); |
| | 170 | return size; |
| | 171 | } |
| | 172 | |
| | 173 | // Some c libs like newlib default buffering to just 1k which is less than |
| | 174 | // cd sector size which is bad for performance. |
| | 175 | // Note that NULL setvbuf() is implemented differently by different libs |
| | 176 | // (newlib mallocs a buffer of given size and glibc ignores size and uses it's own). |
| | 177 | static void set_static_stdio_buffer(FILE *f) |
| | 178 | { |
| | 179 | #if !defined(fopen) // no stdio redirect |
| | 180 | static char buf[16 * 1024]; |
| | 181 | if (f) { |
| | 182 | int r; |
| | 183 | errno = 0; |
| | 184 | r = setvbuf(f, buf, _IOFBF, sizeof(buf)); |
| | 185 | if (r) |
| | 186 | SysPrintf("cdriso: setvbuf %d %d\n", r, errno); |
| | 187 | } |
| | 188 | #endif |
| | 189 | } |
| | 190 | |
| | 191 | // this function tries to get the .toc file of the given .bin |
| | 192 | // the necessary data is put into the ti (trackinformation)-array |
| | 193 | static int parsetoc(const char *isofile) { |
| | 194 | char tocname[MAXPATHLEN]; |
| | 195 | FILE *fi; |
| | 196 | char linebuf[256], tmp[256], name[256]; |
| | 197 | char *token; |
| | 198 | char time[20], time2[20]; |
| | 199 | unsigned int t, sector_offs, sector_size; |
| | 200 | unsigned int current_zero_gap = 0; |
| | 201 | |
| | 202 | numtracks = 0; |
| | 203 | |
| | 204 | // copy name of the iso and change extension from .bin to .toc |
| | 205 | strncpy(tocname, isofile, sizeof(tocname)); |
| | 206 | tocname[MAXPATHLEN - 1] = '\0'; |
| | 207 | if (strlen(tocname) >= 4) { |
| | 208 | strcpy(tocname + strlen(tocname) - 4, ".toc"); |
| | 209 | } |
| | 210 | else { |
| | 211 | return -1; |
| | 212 | } |
| | 213 | |
| | 214 | if ((fi = fopen(tocname, "r")) == NULL) { |
| | 215 | // try changing extension to .cue (to satisfy some stupid tutorials) |
| | 216 | strcpy(tocname + strlen(tocname) - 4, ".cue"); |
| | 217 | if ((fi = fopen(tocname, "r")) == NULL) { |
| | 218 | // if filename is image.toc.bin, try removing .bin (for Brasero) |
| | 219 | strcpy(tocname, isofile); |
| | 220 | t = strlen(tocname); |
| | 221 | if (t >= 8 && strcmp(tocname + t - 8, ".toc.bin") == 0) { |
| | 222 | tocname[t - 4] = '\0'; |
| | 223 | if ((fi = fopen(tocname, "r")) == NULL) { |
| | 224 | return -1; |
| | 225 | } |
| | 226 | } |
| | 227 | else { |
| | 228 | return -1; |
| | 229 | } |
| | 230 | } |
| | 231 | // check if it's really a TOC named as a .cue |
| | 232 | if (fgets(linebuf, sizeof(linebuf), fi) != NULL) { |
| | 233 | token = strtok(linebuf, " "); |
| | 234 | if (token && strncmp(token, "CD", 2) != 0) { |
| | 235 | // && strcmp(token, "CATALOG") != 0) - valid for a real .cue |
| | 236 | fclose(fi); |
| | 237 | return -1; |
| | 238 | } |
| | 239 | } |
| | 240 | fseek(fi, 0, SEEK_SET); |
| | 241 | } |
| | 242 | |
| | 243 | memset(&ti, 0, sizeof(ti)); |
| | 244 | cddaBigEndian = TRUE; // cdrdao uses big-endian for CD Audio |
| | 245 | |
| | 246 | sector_size = CD_FRAMESIZE_RAW; |
| | 247 | sector_offs = 2 * 75; |
| | 248 | |
| | 249 | // parse the .toc file |
| | 250 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { |
| | 251 | // search for tracks |
| | 252 | strncpy(tmp, linebuf, sizeof(linebuf)); |
| | 253 | token = strtok(tmp, " "); |
| | 254 | |
| | 255 | if (token == NULL) continue; |
| | 256 | |
| | 257 | if (!strcmp(token, "TRACK")) { |
| | 258 | sector_offs += current_zero_gap; |
| | 259 | current_zero_gap = 0; |
| | 260 | |
| | 261 | // get type of track |
| | 262 | token = strtok(NULL, " "); |
| | 263 | numtracks++; |
| | 264 | |
| | 265 | if (!strncmp(token, "MODE2_RAW", 9)) { |
| | 266 | ti[numtracks].type = CDRT_DATA; |
| | 267 | ti[numtracks].start = 2 * 75; // assume data track on 0:2:0 |
| | 268 | |
| | 269 | // check if this image contains mixed subchannel data |
| | 270 | token = strtok(NULL, " "); |
| | 271 | if (token != NULL && !strncmp(token, "RW", 2)) { |
| | 272 | sector_size = CD_FRAMESIZE_RAW + SUB_FRAMESIZE; |
| | 273 | subChanMixed = TRUE; |
| | 274 | if (!strncmp(token, "RW_RAW", 6)) |
| | 275 | subChanRaw = TRUE; |
| | 276 | } |
| | 277 | } |
| | 278 | else if (!strncmp(token, "AUDIO", 5)) { |
| | 279 | ti[numtracks].type = CDRT_CDDA; |
| | 280 | } |
| | 281 | } |
| | 282 | else if (!strcmp(token, "DATAFILE")) { |
| | 283 | char msf[3]; |
| | 284 | if (ti[numtracks].type == CDRT_CDDA) { |
| | 285 | sscanf(linebuf, "DATAFILE \"%[^\"]\" #%d %8s", name, &t, time2); |
| | 286 | ti[numtracks].start_offset = t; |
| | 287 | ti[numtracks].start = t / sector_size + sector_offs; |
| | 288 | } |
| | 289 | else { |
| | 290 | sscanf(linebuf, "DATAFILE \"%[^\"]\" %8s", name, time2); |
| | 291 | } |
| | 292 | tok2msf(time2, msf); |
| | 293 | ti[numtracks].length = msf2sec(msf); |
| | 294 | } |
| | 295 | else if (!strcmp(token, "FILE")) { |
| | 296 | char msf[3]; |
| | 297 | sscanf(linebuf, "FILE \"%[^\"]\" #%d %8s %8s", name, &t, time, time2); |
| | 298 | tok2msf(time, msf); |
| | 299 | t += msf2sec(msf) * sector_size; |
| | 300 | ti[numtracks].start_offset = t; |
| | 301 | ti[numtracks].start = t / sector_size + sector_offs; |
| | 302 | tok2msf(time2, msf); |
| | 303 | ti[numtracks].length = msf2sec(msf); |
| | 304 | } |
| | 305 | else if (!strcmp(token, "ZERO") || !strcmp(token, "SILENCE")) { |
| | 306 | // skip unneeded optional fields |
| | 307 | while (token != NULL) { |
| | 308 | token = strtok(NULL, " "); |
| | 309 | if (strchr(token, ':') != NULL) |
| | 310 | break; |
| | 311 | } |
| | 312 | if (token != NULL) { |
| | 313 | tok2msf(token, tmp); |
| | 314 | current_zero_gap = msf2sec(tmp); |
| | 315 | } |
| | 316 | if (numtracks > 1) { |
| | 317 | t = ti[numtracks - 1].start_offset; |
| | 318 | t /= sector_size; |
| | 319 | } |
| | 320 | } |
| | 321 | else if (!strcmp(token, "START")) { |
| | 322 | token = strtok(NULL, " "); |
| | 323 | if (token != NULL && strchr(token, ':')) { |
| | 324 | tok2msf(token, tmp); |
| | 325 | t = msf2sec(tmp); |
| | 326 | ti[numtracks].start_offset += (t - current_zero_gap) * sector_size; |
| | 327 | ti[numtracks].start += t; |
| | 328 | } |
| | 329 | } |
| | 330 | } |
| | 331 | |
| | 332 | fclose(fi); |
| | 333 | |
| | 334 | return 0; |
| | 335 | } |
| | 336 | |
| | 337 | // this function tries to get the .cue file of the given .bin |
| | 338 | // the necessary data is put into the ti (trackinformation)-array |
| | 339 | static int parsecue(const char *isofile) { |
| | 340 | char cuename[MAXPATHLEN]; |
| | 341 | char filepath[MAXPATHLEN]; |
| | 342 | char *incue_fname; |
| | 343 | FILE *fi; |
| | 344 | FILE *ftmp = NULL; |
| | 345 | char *token; |
| | 346 | char time[20]; |
| | 347 | char *tmp; |
| | 348 | char linebuf[256], tmpb[256], dummy[256]; |
| | 349 | unsigned int incue_max_len; |
| | 350 | unsigned int t, mode, toc_sector; |
| | 351 | struct { |
| | 352 | int index[2], pregap, postgap, sector_size; |
| | 353 | } tmpinfo[MAXTRACKS]; |
| | 354 | int i; |
| | 355 | |
| | 356 | numtracks = 0; |
| | 357 | |
| | 358 | // copy name of the iso and change extension from .bin to .cue |
| | 359 | strncpy(cuename, isofile, sizeof(cuename)); |
| | 360 | cuename[MAXPATHLEN - 1] = '\0'; |
| | 361 | if (strlen(cuename) < 4) |
| | 362 | return -1; |
| | 363 | if (strcasecmp(cuename + strlen(cuename) - 4, ".cue") == 0) { |
| | 364 | // it's already open as cdHandle |
| | 365 | fi = cdHandle; |
| | 366 | } |
| | 367 | else { |
| | 368 | // If 'isofile' is a '.cd<X>' file, use it as a .cue file |
| | 369 | // and don't try to search the additional .cue file |
| | 370 | if (strncasecmp(cuename + strlen(cuename) - 4, ".cd", 3) != 0 ) |
| | 371 | strcpy(cuename + strlen(cuename) - 4, ".cue"); |
| | 372 | |
| | 373 | if ((ftmp = fopen(cuename, "r")) == NULL) |
| | 374 | return -1; |
| | 375 | fi = ftmp; |
| | 376 | } |
| | 377 | |
| | 378 | // Some stupid tutorials wrongly tell users to use cdrdao to rip a |
| | 379 | // "bin/cue" image, which is in fact a "bin/toc" image. So let's check |
| | 380 | // that... |
| | 381 | if (fgets(linebuf, sizeof(linebuf), fi) != NULL) { |
| | 382 | if (!strncmp(linebuf, "CD_ROM_XA", 9)) { |
| | 383 | // Don't proceed further, as this is actually a .toc file rather |
| | 384 | // than a .cue file. |
| | 385 | if (ftmp) |
| | 386 | fclose(ftmp); |
| | 387 | return parsetoc(isofile); |
| | 388 | } |
| | 389 | rewind(fi); |
| | 390 | } |
| | 391 | |
| | 392 | // build a path for files referenced in .cue |
| | 393 | strncpy(filepath, cuename, sizeof(filepath)); |
| | 394 | tmp = strrchr(filepath, '/'); |
| | 395 | if (tmp == NULL) |
| | 396 | tmp = strrchr(filepath, '\\'); |
| | 397 | if (tmp != NULL) |
| | 398 | tmp++; |
| | 399 | else |
| | 400 | tmp = filepath; |
| | 401 | *tmp = 0; |
| | 402 | filepath[sizeof(filepath) - 1] = 0; |
| | 403 | incue_fname = tmp; |
| | 404 | incue_max_len = sizeof(filepath) - (tmp - filepath) - 1; |
| | 405 | |
| | 406 | memset(&ti, 0, sizeof(ti)); |
| | 407 | |
| | 408 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { |
| | 409 | strncpy(dummy, linebuf, sizeof(linebuf)); |
| | 410 | token = strtok(dummy, " "); |
| | 411 | |
| | 412 | if (token == NULL) { |
| | 413 | continue; |
| | 414 | } |
| | 415 | |
| | 416 | if (!strcmp(token, "TRACK")) { |
| | 417 | if (numtracks >= MAXTRACKS - 1) { |
| | 418 | SysPrintf(".cue: too many tracks?\n"); |
| | 419 | break; |
| | 420 | } |
| | 421 | numtracks++; |
| | 422 | memset(&tmpinfo[numtracks], -1, sizeof(tmpinfo[numtracks])); |
| | 423 | |
| | 424 | if (sscanf(linebuf, " TRACK %02d", &t) == 1 && t != numtracks) |
| | 425 | SysPrintf(".cue: expected track %d, got %d?\n", |
| | 426 | numtracks, t); |
| | 427 | |
| | 428 | if (strstr(linebuf, "AUDIO") != NULL) { |
| | 429 | ti[numtracks].type = CDRT_CDDA; |
| | 430 | tmpinfo[numtracks].sector_size = 2352; |
| | 431 | } |
| | 432 | else if (sscanf(linebuf, " TRACK %u MODE%u/%u", &t, &mode, |
| | 433 | &tmpinfo[numtracks].sector_size) == 3) |
| | 434 | ti[numtracks].type = CDRT_DATA; |
| | 435 | else { |
| | 436 | SysPrintf(".cue: failed to parse TRACK\n"); |
| | 437 | ti[numtracks].type = numtracks == 1 ? CDRT_DATA : CDRT_CDDA; |
| | 438 | } |
| | 439 | if (tmpinfo[numtracks].sector_size <= 0) |
| | 440 | tmpinfo[numtracks].sector_size = 2352; |
| | 441 | } |
| | 442 | else if (!strcmp(token, "INDEX")) { |
| | 443 | unsigned int index; |
| | 444 | char msf[3]; |
| | 445 | if (sscanf(linebuf, " INDEX %02d %8s", &index, time) != 2) { |
| | 446 | SysPrintf(".cue: failed to parse INDEX\n"); |
| | 447 | continue; |
| | 448 | } |
| | 449 | if (index > 1u) |
| | 450 | continue; |
| | 451 | tok2msf(time, msf); |
| | 452 | tmpinfo[numtracks].index[index] = msf2sec(msf); |
| | 453 | } |
| | 454 | else if (!strcmp(token, "PREGAP")) { |
| | 455 | if (sscanf(linebuf, " PREGAP %8s", time) != 1) { |
| | 456 | SysPrintf(".cue: failed to parse PREGAP\n"); |
| | 457 | continue; |
| | 458 | } |
| | 459 | tok2msf(time, dummy); |
| | 460 | tmpinfo[numtracks].pregap = msf2sec(dummy); |
| | 461 | } |
| | 462 | else if (!strcmp(token, "POSTGAP")) { |
| | 463 | if (sscanf(linebuf, " POSTGAP %8s", time) != 1) { |
| | 464 | SysPrintf(".cue: failed to parse POSTGAP\n"); |
| | 465 | continue; |
| | 466 | } |
| | 467 | tok2msf(time, dummy); |
| | 468 | tmpinfo[numtracks].postgap = msf2sec(dummy); |
| | 469 | } |
| | 470 | else if (!strcmp(token, "FILE")) { |
| | 471 | t = sscanf(linebuf, " FILE \"%255[^\"]\"", tmpb); |
| | 472 | if (t != 1) |
| | 473 | sscanf(linebuf, " FILE %255s", tmpb); |
| | 474 | |
| | 475 | // absolute path? |
| | 476 | ti[numtracks + 1].handle = fopen(tmpb, "rb"); |
| | 477 | if (ti[numtracks + 1].handle == NULL) { |
| | 478 | // relative to .cue? |
| | 479 | tmp = strrchr(tmpb, '\\'); |
| | 480 | if (tmp == NULL) |
| | 481 | tmp = strrchr(tmpb, '/'); |
| | 482 | if (tmp != NULL) |
| | 483 | tmp++; |
| | 484 | else |
| | 485 | tmp = tmpb; |
| | 486 | strncpy(incue_fname, tmp, incue_max_len); |
| | 487 | ti[numtracks + 1].handle = fopen(filepath, "rb"); |
| | 488 | } |
| | 489 | if (ti[numtracks + 1].handle == NULL) { |
| | 490 | SysPrintf(_("\ncould not open: %s\n"), filepath); |
| | 491 | continue; |
| | 492 | } |
| | 493 | |
| | 494 | if (numtracks + 1 > 1) |
| | 495 | multifile = 1; |
| | 496 | } |
| | 497 | } |
| | 498 | |
| | 499 | if (ftmp) |
| | 500 | fclose(ftmp); |
| | 501 | |
| | 502 | // if there are no tracks detected, then it's not a cue file |
| | 503 | if (!numtracks) |
| | 504 | return -1; |
| | 505 | |
| | 506 | for (i = 1; i <= numtracks; i++) { |
| | 507 | if (tmpinfo[i].index[1] == -1) { |
| | 508 | SysPrintf(".cue: no INDEX 01 for track %d?\n", i); |
| | 509 | tmpinfo[i].index[1] = 0; |
| | 510 | if (tmpinfo[i].index[0] != -1) |
| | 511 | tmpinfo[i].index[1] = tmpinfo[i].index[0] + 2 * 75; |
| | 512 | } |
| | 513 | } |
| | 514 | |
| | 515 | // complete the actual toc |
| | 516 | tmpinfo[0].postgap = -1; |
| | 517 | ftmp = cdHandle; |
| | 518 | toc_sector = 2*75; |
| | 519 | for (i = 1; i <= numtracks; i++) |
| | 520 | { |
| | 521 | unsigned int pregap = 0, sector_size = tmpinfo[i].sector_size; |
| | 522 | // various ways to specify pregap in a .cue |
| | 523 | if (tmpinfo[i].pregap != -1) |
| | 524 | pregap += tmpinfo[i].pregap; |
| | 525 | if (tmpinfo[i-1].postgap != -1) |
| | 526 | pregap += tmpinfo[i-1].postgap; |
| | 527 | if (ti[i].handle && tmpinfo[i].index[0] != -1) |
| | 528 | pregap += tmpinfo[i].index[1] - tmpinfo[i].index[0]; |
| | 529 | |
| | 530 | toc_sector += pregap; |
| | 531 | ti[i].start = toc_sector; |
| | 532 | ti[i].start_offset = tmpinfo[i].index[1] * sector_size; |
| | 533 | |
| | 534 | if (ti[i].handle) |
| | 535 | ftmp = ti[i].handle; |
| | 536 | if (i+1 <= numtracks && ti[i+1].handle == NULL) { |
| | 537 | // this track and the next one share the backing file |
| | 538 | ti[i].length = tmpinfo[i+1].index[1] - tmpinfo[i].index[1]; |
| | 539 | } |
| | 540 | else { |
| | 541 | size_t file_size = get_size(ftmp); |
| | 542 | long left = file_size - ti[i].start_offset; |
| | 543 | if (left > 0) |
| | 544 | ti[i].length = left / sector_size; |
| | 545 | else |
| | 546 | ti[i].length = 0; |
| | 547 | } |
| | 548 | toc_sector += ti[i].length; |
| | 549 | } |
| | 550 | |
| | 551 | // the data track handle is always in cdHandle |
| | 552 | if (ti[1].handle) { |
| | 553 | fclose(cdHandle); |
| | 554 | cdHandle = ti[1].handle; |
| | 555 | ti[1].handle = NULL; |
| | 556 | set_static_stdio_buffer(cdHandle); |
| | 557 | } |
| | 558 | return 0; |
| | 559 | } |
| | 560 | |
| | 561 | // this function tries to get the .ccd file of the given .img |
| | 562 | // the necessary data is put into the ti (trackinformation)-array |
| | 563 | static int parseccd(const char *isofile) { |
| | 564 | char ccdname[MAXPATHLEN]; |
| | 565 | FILE *fi; |
| | 566 | char linebuf[256]; |
| | 567 | unsigned int t; |
| | 568 | |
| | 569 | numtracks = 0; |
| | 570 | |
| | 571 | // copy name of the iso and change extension from .img to .ccd |
| | 572 | strncpy(ccdname, isofile, sizeof(ccdname)); |
| | 573 | ccdname[MAXPATHLEN - 1] = '\0'; |
| | 574 | if (strlen(ccdname) >= 4) { |
| | 575 | strcpy(ccdname + strlen(ccdname) - 4, ".ccd"); |
| | 576 | } |
| | 577 | else { |
| | 578 | return -1; |
| | 579 | } |
| | 580 | |
| | 581 | if ((fi = fopen(ccdname, "r")) == NULL) { |
| | 582 | return -1; |
| | 583 | } |
| | 584 | |
| | 585 | memset(&ti, 0, sizeof(ti)); |
| | 586 | |
| | 587 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { |
| | 588 | if (!strncmp(linebuf, "[TRACK", 6)){ |
| | 589 | numtracks++; |
| | 590 | } |
| | 591 | else if (!strncmp(linebuf, "MODE=", 5)) { |
| | 592 | sscanf(linebuf, "MODE=%d", &t); |
| | 593 | ti[numtracks].type = (t == 0) ? CDRT_CDDA : CDRT_DATA; |
| | 594 | } |
| | 595 | else if (!strncmp(linebuf, "INDEX 1=", 8)) { |
| | 596 | sscanf(linebuf, "INDEX 1=%d", &t); |
| | 597 | ti[numtracks].start = 2 * 75 + t; |
| | 598 | ti[numtracks].start_offset = t * 2352; |
| | 599 | |
| | 600 | // If we've already seen another track, this is its end |
| | 601 | if (numtracks > 1) { |
| | 602 | t = ti[numtracks].start - ti[numtracks - 1].start; |
| | 603 | ti[numtracks - 1].length = t; |
| | 604 | } |
| | 605 | } |
| | 606 | } |
| | 607 | |
| | 608 | fclose(fi); |
| | 609 | |
| | 610 | // Fill out the last track's end based on size |
| | 611 | if (numtracks >= 1) { |
| | 612 | t = get_size(cdHandle) / 2352 - ti[numtracks].start + 2 * 75; |
| | 613 | ti[numtracks].length = t; |
| | 614 | } |
| | 615 | |
| | 616 | return 0; |
| | 617 | } |
| | 618 | |
| | 619 | // this function tries to get the .mds file of the given .mdf |
| | 620 | // the necessary data is put into the ti (trackinformation)-array |
| | 621 | static int parsemds(const char *isofile) { |
| | 622 | char mdsname[MAXPATHLEN]; |
| | 623 | FILE *fi; |
| | 624 | unsigned int offset, extra_offset, l, i; |
| | 625 | unsigned short s; |
| | 626 | |
| | 627 | numtracks = 0; |
| | 628 | |
| | 629 | // copy name of the iso and change extension from .mdf to .mds |
| | 630 | strncpy(mdsname, isofile, sizeof(mdsname)); |
| | 631 | mdsname[MAXPATHLEN - 1] = '\0'; |
| | 632 | if (strlen(mdsname) >= 4) { |
| | 633 | strcpy(mdsname + strlen(mdsname) - 4, ".mds"); |
| | 634 | } |
| | 635 | else { |
| | 636 | return -1; |
| | 637 | } |
| | 638 | |
| | 639 | if ((fi = fopen(mdsname, "rb")) == NULL) { |
| | 640 | return -1; |
| | 641 | } |
| | 642 | |
| | 643 | memset(&ti, 0, sizeof(ti)); |
| | 644 | |
| | 645 | // check if it's a valid mds file |
| | 646 | if (fread(&i, 1, sizeof(i), fi) != sizeof(i)) |
| | 647 | goto fail_io; |
| | 648 | i = SWAP32(i); |
| | 649 | if (i != 0x4944454D) { |
| | 650 | // not an valid mds file |
| | 651 | fclose(fi); |
| | 652 | return -1; |
| | 653 | } |
| | 654 | |
| | 655 | // get offset to session block |
| | 656 | fseek(fi, 0x50, SEEK_SET); |
| | 657 | if (fread(&offset, 1, sizeof(offset), fi) != sizeof(offset)) |
| | 658 | goto fail_io; |
| | 659 | offset = SWAP32(offset); |
| | 660 | |
| | 661 | // get total number of tracks |
| | 662 | offset += 14; |
| | 663 | fseek(fi, offset, SEEK_SET); |
| | 664 | if (fread(&s, 1, sizeof(s), fi) != sizeof(s)) |
| | 665 | goto fail_io; |
| | 666 | s = SWAP16(s); |
| | 667 | numtracks = s; |
| | 668 | |
| | 669 | // get offset to track blocks |
| | 670 | fseek(fi, 4, SEEK_CUR); |
| | 671 | if (fread(&offset, 1, sizeof(offset), fi) != sizeof(offset)) |
| | 672 | goto fail_io; |
| | 673 | offset = SWAP32(offset); |
| | 674 | |
| | 675 | // skip lead-in data |
| | 676 | while (1) { |
| | 677 | fseek(fi, offset + 4, SEEK_SET); |
| | 678 | if (fgetc(fi) < 0xA0) { |
| | 679 | break; |
| | 680 | } |
| | 681 | offset += 0x50; |
| | 682 | } |
| | 683 | |
| | 684 | // check if the image contains mixed subchannel data |
| | 685 | fseek(fi, offset + 1, SEEK_SET); |
| | 686 | subChanMixed = subChanRaw = (fgetc(fi) ? TRUE : FALSE); |
| | 687 | |
| | 688 | // read track data |
| | 689 | for (i = 1; i <= numtracks; i++) { |
| | 690 | char msf[3]; |
| | 691 | fseek(fi, offset, SEEK_SET); |
| | 692 | |
| | 693 | // get the track type |
| | 694 | ti[i].type = (fgetc(fi) == 0xA9) ? CDRT_CDDA : CDRT_DATA; |
| | 695 | fseek(fi, 8, SEEK_CUR); |
| | 696 | |
| | 697 | // get the track starting point |
| | 698 | msf[0] = fgetc(fi); |
| | 699 | msf[1] = fgetc(fi); |
| | 700 | msf[2] = fgetc(fi); |
| | 701 | ti[i].start = msf2sec(msf); |
| | 702 | |
| | 703 | if (fread(&extra_offset, 1, sizeof(extra_offset), fi) != sizeof(extra_offset)) |
| | 704 | goto fail_io; |
| | 705 | extra_offset = SWAP32(extra_offset); |
| | 706 | |
| | 707 | // get track start offset (in .mdf) |
| | 708 | fseek(fi, offset + 0x28, SEEK_SET); |
| | 709 | if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) |
| | 710 | goto fail_io; |
| | 711 | l = SWAP32(l); |
| | 712 | ti[i].start_offset = l; |
| | 713 | |
| | 714 | // get pregap |
| | 715 | fseek(fi, extra_offset, SEEK_SET); |
| | 716 | if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) |
| | 717 | goto fail_io; |
| | 718 | l = SWAP32(l); |
| | 719 | |
| | 720 | // get the track length |
| | 721 | if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) |
| | 722 | goto fail_io; |
| | 723 | ti[i].length = SWAP32(l); |
| | 724 | |
| | 725 | offset += 0x50; |
| | 726 | } |
| | 727 | fclose(fi); |
| | 728 | return 0; |
| | 729 | fail_io: |
| | 730 | #ifndef NDEBUG |
| | 731 | SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); |
| | 732 | #endif |
| | 733 | fclose(fi); |
| | 734 | return -1; |
| | 735 | } |
| | 736 | |
| | 737 | static int handlepbp(const char *isofile) { |
| | 738 | struct { |
| | 739 | unsigned int sig; |
| | 740 | unsigned int dontcare[8]; |
| | 741 | unsigned int psar_offs; |
| | 742 | } pbp_hdr; |
| | 743 | struct { |
| | 744 | unsigned char type; |
| | 745 | unsigned char pad0; |
| | 746 | unsigned char track; |
| | 747 | char index0[3]; |
| | 748 | char pad1; |
| | 749 | char index1[3]; |
| | 750 | } toc_entry; |
| | 751 | struct { |
| | 752 | unsigned int offset; |
| | 753 | unsigned int size; |
| | 754 | unsigned int dontcare[6]; |
| | 755 | } index_entry; |
| | 756 | char psar_sig[11]; |
| | 757 | off_t psisoimg_offs, cdimg_base; |
| | 758 | unsigned int cd_length; |
| | 759 | unsigned int offsettab[8]; |
| | 760 | unsigned int psar_offs, index_entry_size, index_entry_offset; |
| | 761 | const char *ext = NULL; |
| | 762 | int i, ret; |
| | 763 | |
| | 764 | if (strlen(isofile) >= 4) |
| | 765 | ext = isofile + strlen(isofile) - 4; |
| | 766 | if (ext == NULL || strcasecmp(ext, ".pbp") != 0) |
| | 767 | return -1; |
| | 768 | |
| | 769 | numtracks = 0; |
| | 770 | |
| | 771 | ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), cdHandle); |
| | 772 | if (ret != sizeof(pbp_hdr)) { |
| | 773 | SysPrintf("failed to read pbp\n"); |
| | 774 | goto fail_io; |
| | 775 | } |
| | 776 | |
| | 777 | psar_offs = SWAP32(pbp_hdr.psar_offs); |
| | 778 | |
| | 779 | ret = fseeko(cdHandle, psar_offs, SEEK_SET); |
| | 780 | if (ret != 0) { |
| | 781 | SysPrintf("failed to seek to %x\n", psar_offs); |
| | 782 | goto fail_io; |
| | 783 | } |
| | 784 | |
| | 785 | psisoimg_offs = psar_offs; |
| | 786 | if (fread(psar_sig, 1, sizeof(psar_sig), cdHandle) != sizeof(psar_sig)) |
| | 787 | goto fail_io; |
| | 788 | psar_sig[10] = 0; |
| | 789 | if (strcmp(psar_sig, "PSTITLEIMG") == 0) { |
| | 790 | // multidisk image? |
| | 791 | ret = fseeko(cdHandle, psar_offs + 0x200, SEEK_SET); |
| | 792 | if (ret != 0) { |
| | 793 | SysPrintf("failed to seek to %x\n", psar_offs + 0x200); |
| | 794 | goto fail_io; |
| | 795 | } |
| | 796 | |
| | 797 | if (fread(&offsettab, 1, sizeof(offsettab), cdHandle) != sizeof(offsettab)) { |
| | 798 | SysPrintf("failed to read offsettab\n"); |
| | 799 | goto fail_io; |
| | 800 | } |
| | 801 | |
| | 802 | for (i = 0; i < sizeof(offsettab) / sizeof(offsettab[0]); i++) { |
| | 803 | if (offsettab[i] == 0) |
| | 804 | break; |
| | 805 | } |
| | 806 | cdrIsoMultidiskCount = i; |
| | 807 | if (cdrIsoMultidiskCount == 0) { |
| | 808 | SysPrintf("multidisk eboot has 0 images?\n"); |
| | 809 | goto fail_io; |
| | 810 | } |
| | 811 | |
| | 812 | if (cdrIsoMultidiskSelect >= cdrIsoMultidiskCount) |
| | 813 | cdrIsoMultidiskSelect = 0; |
| | 814 | |
| | 815 | psisoimg_offs += SWAP32(offsettab[cdrIsoMultidiskSelect]); |
| | 816 | |
| | 817 | ret = fseeko(cdHandle, psisoimg_offs, SEEK_SET); |
| | 818 | if (ret != 0) { |
| | 819 | SysPrintf("failed to seek to %llx\n", (long long)psisoimg_offs); |
| | 820 | goto fail_io; |
| | 821 | } |
| | 822 | |
| | 823 | if (fread(psar_sig, 1, sizeof(psar_sig), cdHandle) != sizeof(psar_sig)) |
| | 824 | goto fail_io; |
| | 825 | psar_sig[10] = 0; |
| | 826 | } |
| | 827 | |
| | 828 | if (strcmp(psar_sig, "PSISOIMG00") != 0) { |
| | 829 | SysPrintf("bad psar_sig: %s\n", psar_sig); |
| | 830 | goto fail_io; |
| | 831 | } |
| | 832 | |
| | 833 | // seek to TOC |
| | 834 | ret = fseeko(cdHandle, psisoimg_offs + 0x800, SEEK_SET); |
| | 835 | if (ret != 0) { |
| | 836 | SysPrintf("failed to seek to %llx\n", (long long)psisoimg_offs + 0x800); |
| | 837 | goto fail_io; |
| | 838 | } |
| | 839 | |
| | 840 | // first 3 entries are special |
| | 841 | fseek(cdHandle, sizeof(toc_entry), SEEK_CUR); |
| | 842 | if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) |
| | 843 | goto fail_io; |
| | 844 | numtracks = btoi(toc_entry.index1[0]); |
| | 845 | |
| | 846 | if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) |
| | 847 | goto fail_io; |
| | 848 | cd_length = btoi(toc_entry.index1[0]) * 60 * 75 + |
| | 849 | btoi(toc_entry.index1[1]) * 75 + btoi(toc_entry.index1[2]); |
| | 850 | |
| | 851 | for (i = 1; i <= numtracks; i++) { |
| | 852 | char msf[3]; |
| | 853 | if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) |
| | 854 | goto fail_io; |
| | 855 | |
| | 856 | ti[i].type = (toc_entry.type == 1) ? CDRT_CDDA : CDRT_DATA; |
| | 857 | |
| | 858 | ti[i].start_offset = btoi(toc_entry.index0[0]) * 60 * 75 + |
| | 859 | btoi(toc_entry.index0[1]) * 75 + btoi(toc_entry.index0[2]); |
| | 860 | ti[i].start_offset *= 2352; |
| | 861 | msf[0] = btoi(toc_entry.index1[0]); |
| | 862 | msf[1] = btoi(toc_entry.index1[1]); |
| | 863 | msf[2] = btoi(toc_entry.index1[2]); |
| | 864 | ti[i].start = msf2sec(msf); |
| | 865 | |
| | 866 | if (i > 1) |
| | 867 | ti[i - 1].length = ti[i].start - ti[i - 1].start; |
| | 868 | } |
| | 869 | ti[numtracks].length = cd_length - ti[numtracks].start_offset / 2352; |
| | 870 | |
| | 871 | // seek to ISO index |
| | 872 | ret = fseeko(cdHandle, psisoimg_offs + 0x4000, SEEK_SET); |
| | 873 | if (ret != 0) { |
| | 874 | SysPrintf("failed to seek to ISO index\n"); |
| | 875 | goto fail_io; |
| | 876 | } |
| | 877 | |
| | 878 | compr_img = calloc(1, sizeof(*compr_img)); |
| | 879 | if (compr_img == NULL) |
| | 880 | goto fail_io; |
| | 881 | |
| | 882 | compr_img->block_shift = 4; |
| | 883 | compr_img->current_block = (unsigned int)-1; |
| | 884 | |
| | 885 | compr_img->index_len = (0x100000 - 0x4000) / sizeof(index_entry); |
| | 886 | compr_img->index_table = malloc((compr_img->index_len + 1) * sizeof(compr_img->index_table[0])); |
| | 887 | if (compr_img->index_table == NULL) |
| | 888 | goto fail_io; |
| | 889 | |
| | 890 | cdimg_base = psisoimg_offs + 0x100000; |
| | 891 | for (i = 0; i < compr_img->index_len; i++) { |
| | 892 | ret = fread(&index_entry, 1, sizeof(index_entry), cdHandle); |
| | 893 | if (ret != sizeof(index_entry)) { |
| | 894 | SysPrintf("failed to read index_entry #%d\n", i); |
| | 895 | goto fail_index; |
| | 896 | } |
| | 897 | |
| | 898 | index_entry_size = SWAP32(index_entry.size); |
| | 899 | index_entry_offset = SWAP32(index_entry.offset); |
| | 900 | |
| | 901 | if (index_entry_size == 0) |
| | 902 | break; |
| | 903 | |
| | 904 | compr_img->index_table[i] = cdimg_base + index_entry_offset; |
| | 905 | } |
| | 906 | compr_img->index_table[i] = cdimg_base + index_entry_offset + index_entry_size; |
| | 907 | |
| | 908 | return 0; |
| | 909 | |
| | 910 | fail_index: |
| | 911 | free(compr_img->index_table); |
| | 912 | compr_img->index_table = NULL; |
| | 913 | goto done; |
| | 914 | |
| | 915 | fail_io: |
| | 916 | SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); |
| | 917 | rewind(cdHandle); |
| | 918 | |
| | 919 | done: |
| | 920 | if (compr_img != NULL) { |
| | 921 | free(compr_img); |
| | 922 | compr_img = NULL; |
| | 923 | } |
| | 924 | return -1; |
| | 925 | } |
| | 926 | |
| | 927 | static int handlecbin(const char *isofile) { |
| | 928 | struct |
| | 929 | { |
| | 930 | char magic[4]; |
| | 931 | unsigned int header_size; |
| | 932 | unsigned long long total_bytes; |
| | 933 | unsigned int block_size; |
| | 934 | unsigned char ver; // 1 |
| | 935 | unsigned char align; |
| | 936 | unsigned char rsv_06[2]; |
| | 937 | } ciso_hdr; |
| | 938 | const char *ext = NULL; |
| | 939 | unsigned int *index_table = NULL; |
| | 940 | unsigned int index = 0, plain; |
| | 941 | int i, ret; |
| | 942 | |
| | 943 | if (strlen(isofile) >= 5) |
| | 944 | ext = isofile + strlen(isofile) - 5; |
| | 945 | if (ext == NULL || (strcasecmp(ext + 1, ".cbn") != 0 && strcasecmp(ext, ".cbin") != 0)) |
| | 946 | return -1; |
| | 947 | |
| | 948 | ret = fread(&ciso_hdr, 1, sizeof(ciso_hdr), cdHandle); |
| | 949 | if (ret != sizeof(ciso_hdr)) { |
| | 950 | SysPrintf("failed to read ciso header\n"); |
| | 951 | goto fail_io; |
| | 952 | } |
| | 953 | |
| | 954 | if (strncmp(ciso_hdr.magic, "CISO", 4) != 0 || ciso_hdr.total_bytes <= 0 || ciso_hdr.block_size <= 0) { |
| | 955 | SysPrintf("bad ciso header\n"); |
| | 956 | goto fail_io; |
| | 957 | } |
| | 958 | if (ciso_hdr.header_size != 0 && ciso_hdr.header_size != sizeof(ciso_hdr)) { |
| | 959 | ret = fseeko(cdHandle, ciso_hdr.header_size, SEEK_SET); |
| | 960 | if (ret != 0) { |
| | 961 | SysPrintf("failed to seek to %x\n", ciso_hdr.header_size); |
| | 962 | goto fail_io; |
| | 963 | } |
| | 964 | } |
| | 965 | |
| | 966 | compr_img = calloc(1, sizeof(*compr_img)); |
| | 967 | if (compr_img == NULL) |
| | 968 | goto fail_io; |
| | 969 | |
| | 970 | compr_img->block_shift = 0; |
| | 971 | compr_img->current_block = (unsigned int)-1; |
| | 972 | |
| | 973 | compr_img->index_len = ciso_hdr.total_bytes / ciso_hdr.block_size; |
| | 974 | index_table = malloc((compr_img->index_len + 1) * sizeof(index_table[0])); |
| | 975 | if (index_table == NULL) |
| | 976 | goto fail_io; |
| | 977 | |
| | 978 | ret = fread(index_table, sizeof(index_table[0]), compr_img->index_len, cdHandle); |
| | 979 | if (ret != compr_img->index_len) { |
| | 980 | SysPrintf("failed to read index table\n"); |
| | 981 | goto fail_index; |
| | 982 | } |
| | 983 | |
| | 984 | compr_img->index_table = malloc((compr_img->index_len + 1) * sizeof(compr_img->index_table[0])); |
| | 985 | if (compr_img->index_table == NULL) |
| | 986 | goto fail_index; |
| | 987 | |
| | 988 | for (i = 0; i < compr_img->index_len + 1; i++) { |
| | 989 | index = index_table[i]; |
| | 990 | plain = index & 0x80000000; |
| | 991 | index &= 0x7fffffff; |
| | 992 | compr_img->index_table[i] = (off_t)index << ciso_hdr.align; |
| | 993 | if (plain) |
| | 994 | compr_img->index_table[i] |= OFF_T_MSB; |
| | 995 | } |
| | 996 | |
| | 997 | // fixup the toc and the last track length |
| | 998 | if (numtracks == 0) { |
| | 999 | numtracks = 1; |
| | 1000 | ti[1].type = CDRT_DATA; |
| | 1001 | ti[1].start_offset = 0; |
| | 1002 | ti[1].start = 2 * 75; |
| | 1003 | } |
| | 1004 | ti[numtracks].length = (ciso_hdr.total_bytes - ti[numtracks].start_offset) / 2352; |
| | 1005 | free(index_table); |
| | 1006 | return 0; |
| | 1007 | |
| | 1008 | fail_index: |
| | 1009 | free(index_table); |
| | 1010 | fail_io: |
| | 1011 | if (compr_img != NULL) { |
| | 1012 | free(compr_img); |
| | 1013 | compr_img = NULL; |
| | 1014 | } |
| | 1015 | rewind(cdHandle); |
| | 1016 | return -1; |
| | 1017 | } |
| | 1018 | |
| | 1019 | #ifdef HAVE_CHD |
| | 1020 | static int handlechd(const char *isofile) { |
| | 1021 | int frame_offset = 150; |
| | 1022 | int file_offset = 0; |
| | 1023 | int is_chd_ext = 0; |
| | 1024 | chd_error err; |
| | 1025 | |
| | 1026 | if (strlen(isofile) >= 3) { |
| | 1027 | const char *ext = isofile + strlen(isofile) - 3; |
| | 1028 | is_chd_ext = !strcasecmp(ext, "chd"); |
| | 1029 | } |
| | 1030 | chd_img = calloc(1, sizeof(*chd_img)); |
| | 1031 | if (chd_img == NULL) |
| | 1032 | goto fail_io; |
| | 1033 | |
| | 1034 | err = chd_open_file(cdHandle, CHD_OPEN_READ, NULL, &chd_img->chd); |
| | 1035 | if (err != CHDERR_NONE) { |
| | 1036 | if (is_chd_ext) |
| | 1037 | SysPrintf("chd_open: %d\n", err); |
| | 1038 | goto fail_io; |
| | 1039 | } |
| | 1040 | |
| | 1041 | if (Config.CHD_Precache && (chd_precache(chd_img->chd) != CHDERR_NONE)) |
| | 1042 | goto fail_io; |
| | 1043 | |
| | 1044 | chd_img->header = chd_get_header(chd_img->chd); |
| | 1045 | |
| | 1046 | chd_img->buffer = malloc(chd_img->header->hunkbytes * 2); |
| | 1047 | if (chd_img->buffer == NULL) |
| | 1048 | goto fail_io; |
| | 1049 | |
| | 1050 | chd_img->sectors_per_hunk = chd_img->header->hunkbytes / (CD_FRAMESIZE_RAW + SUB_FRAMESIZE); |
| | 1051 | chd_img->current_hunk[0] = (unsigned int)-1; |
| | 1052 | chd_img->current_hunk[1] = (unsigned int)-1; |
| | 1053 | |
| | 1054 | cddaBigEndian = TRUE; |
| | 1055 | |
| | 1056 | numtracks = 0; |
| | 1057 | memset(ti, 0, sizeof(ti)); |
| | 1058 | |
| | 1059 | while (1) |
| | 1060 | { |
| | 1061 | struct { |
| | 1062 | char type[64]; |
| | 1063 | char subtype[32]; |
| | 1064 | char pgtype[32]; |
| | 1065 | char pgsub[32]; |
| | 1066 | uint32_t track; |
| | 1067 | uint32_t frames; |
| | 1068 | uint32_t pregap; |
| | 1069 | uint32_t postgap; |
| | 1070 | } md = {}; |
| | 1071 | char meta[256]; |
| | 1072 | uint32_t meta_size = 0; |
| | 1073 | |
| | 1074 | if (chd_get_metadata(chd_img->chd, CDROM_TRACK_METADATA2_TAG, numtracks, meta, sizeof(meta), &meta_size, NULL, NULL) == CHDERR_NONE) |
| | 1075 | sscanf(meta, CDROM_TRACK_METADATA2_FORMAT, &md.track, md.type, md.subtype, &md.frames, &md.pregap, md.pgtype, md.pgsub, &md.postgap); |
| | 1076 | else if (chd_get_metadata(chd_img->chd, CDROM_TRACK_METADATA_TAG, numtracks, meta, sizeof(meta), &meta_size, NULL, NULL) == CHDERR_NONE) |
| | 1077 | sscanf(meta, CDROM_TRACK_METADATA_FORMAT, &md.track, md.type, md.subtype, &md.frames); |
| | 1078 | else |
| | 1079 | break; |
| | 1080 | |
| | 1081 | SysPrintf("chd: %s\n", meta); |
| | 1082 | |
| | 1083 | if (md.track == 1) { |
| | 1084 | if (!strncmp(md.subtype, "RW", 2)) { |
| | 1085 | subChanMixed = TRUE; |
| | 1086 | if (!strcmp(md.subtype, "RW_RAW")) |
| | 1087 | subChanRaw = TRUE; |
| | 1088 | } |
| | 1089 | } |
| | 1090 | |
| | 1091 | ti[md.track].type = !strncmp(md.type, "AUDIO", 5) ? CDRT_CDDA : CDRT_DATA; |
| | 1092 | |
| | 1093 | ti[md.track].start = frame_offset + md.pregap; |
| | 1094 | ti[md.track].length = md.frames; |
| | 1095 | |
| | 1096 | ti[md.track].start_offset = file_offset + md.pregap; |
| | 1097 | |
| | 1098 | // XXX: what about postgap? |
| | 1099 | frame_offset += md.frames; |
| | 1100 | file_offset += md.frames; |
| | 1101 | numtracks++; |
| | 1102 | } |
| | 1103 | |
| | 1104 | if (numtracks) |
| | 1105 | return 0; |
| | 1106 | |
| | 1107 | fail_io: |
| | 1108 | if (chd_img != NULL) { |
| | 1109 | free(chd_img->buffer); |
| | 1110 | free(chd_img); |
| | 1111 | chd_img = NULL; |
| | 1112 | } |
| | 1113 | return -1; |
| | 1114 | } |
| | 1115 | #endif |
| | 1116 | |
| | 1117 | // this function tries to get the .sub file of the given .img |
| | 1118 | static int opensubfile(const char *isoname) { |
| | 1119 | char subname[MAXPATHLEN]; |
| | 1120 | |
| | 1121 | // copy name of the iso and change extension from .img to .sub |
| | 1122 | strncpy(subname, isoname, sizeof(subname)); |
| | 1123 | subname[MAXPATHLEN - 1] = '\0'; |
| | 1124 | if (strlen(subname) >= 4) { |
| | 1125 | strcpy(subname + strlen(subname) - 4, ".sub"); |
| | 1126 | } |
| | 1127 | else { |
| | 1128 | return -1; |
| | 1129 | } |
| | 1130 | |
| | 1131 | subHandle = fopen(subname, "rb"); |
| | 1132 | if (subHandle == NULL) |
| | 1133 | return -1; |
| | 1134 | |
| | 1135 | return 0; |
| | 1136 | } |
| | 1137 | |
| | 1138 | static int opensbifile(const char *isoname) { |
| | 1139 | char sbiname[MAXPATHLEN], disknum[MAXPATHLEN] = "0"; |
| | 1140 | |
| | 1141 | strncpy(sbiname, isoname, sizeof(sbiname)); |
| | 1142 | sbiname[MAXPATHLEN - 1] = '\0'; |
| | 1143 | if (strlen(sbiname) >= 4) { |
| | 1144 | if (cdrIsoMultidiskCount > 1) { |
| | 1145 | sprintf(disknum, "_%i.sbi", cdrIsoMultidiskSelect + 1); |
| | 1146 | strcpy(sbiname + strlen(sbiname) - 4, disknum); |
| | 1147 | } |
| | 1148 | else |
| | 1149 | strcpy(sbiname + strlen(sbiname) - 4, ".sbi"); |
| | 1150 | } |
| | 1151 | else { |
| | 1152 | return -1; |
| | 1153 | } |
| | 1154 | |
| | 1155 | return LoadSBI(sbiname, ti[1].length); |
| | 1156 | } |
| | 1157 | |
| | 1158 | static int cdread_normal(FILE *f, unsigned int base, void *dest, int sector) |
| | 1159 | { |
| | 1160 | int ret; |
| | 1161 | if (!f) |
| | 1162 | return -1; |
| | 1163 | if (!dest) |
| | 1164 | dest = cdbuffer; |
| | 1165 | if (fseeko(f, base + sector * CD_FRAMESIZE_RAW, SEEK_SET)) |
| | 1166 | goto fail_io; |
| | 1167 | ret = fread(dest, 1, CD_FRAMESIZE_RAW, f); |
| | 1168 | if (ret <= 0) |
| | 1169 | goto fail_io; |
| | 1170 | return ret; |
| | 1171 | |
| | 1172 | fail_io: |
| | 1173 | // often happens in cdda gaps of a split cue/bin, so not logged |
| | 1174 | //SysPrintf("File IO error %d, base %u, sector %u\n", errno, base, sector); |
| | 1175 | return -1; |
| | 1176 | } |
| | 1177 | |
| | 1178 | static int cdread_sub_mixed(FILE *f, unsigned int base, void *dest, int sector) |
| | 1179 | { |
| | 1180 | int ret; |
| | 1181 | |
| | 1182 | if (!f) |
| | 1183 | return -1; |
| | 1184 | if (!dest) |
| | 1185 | dest = cdbuffer; |
| | 1186 | if (fseeko(f, base + sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE), SEEK_SET)) |
| | 1187 | goto fail_io; |
| | 1188 | ret = fread(dest, 1, CD_FRAMESIZE_RAW, f); |
| | 1189 | if (ret <= 0) |
| | 1190 | goto fail_io; |
| | 1191 | return ret; |
| | 1192 | |
| | 1193 | fail_io: |
| | 1194 | //SysPrintf("File IO error %d, base %u, sector %u\n", errno, base, sector); |
| | 1195 | return -1; |
| | 1196 | } |
| | 1197 | |
| | 1198 | static int cdread_sub_sub_mixed(FILE *f, int sector, void *buffer) |
| | 1199 | { |
| | 1200 | if (!f) |
| | 1201 | return -1; |
| | 1202 | if (fseeko(f, sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + CD_FRAMESIZE_RAW, SEEK_SET)) |
| | 1203 | goto fail_io; |
| | 1204 | if (fread(buffer, 1, SUB_FRAMESIZE, f) != SUB_FRAMESIZE) |
| | 1205 | goto fail_io; |
| | 1206 | |
| | 1207 | return 0; |
| | 1208 | |
| | 1209 | fail_io: |
| | 1210 | SysPrintf("subchannel: file IO error %d, sector %u\n", errno, sector); |
| | 1211 | return -1; |
| | 1212 | } |
| | 1213 | |
| | 1214 | static int uncompress2_pcsx(void *out, unsigned long *out_size, void *in, unsigned long in_size) |
| | 1215 | { |
| | 1216 | static z_stream z; |
| | 1217 | int ret = 0; |
| | 1218 | |
| | 1219 | if (z.zalloc == NULL) { |
| | 1220 | // XXX: one-time leak here.. |
| | 1221 | z.next_in = Z_NULL; |
| | 1222 | z.avail_in = 0; |
| | 1223 | z.zalloc = Z_NULL; |
| | 1224 | z.zfree = Z_NULL; |
| | 1225 | z.opaque = Z_NULL; |
| | 1226 | ret = inflateInit2(&z, -15); |
| | 1227 | } |
| | 1228 | else |
| | 1229 | ret = inflateReset(&z); |
| | 1230 | if (ret != Z_OK) |
| | 1231 | return ret; |
| | 1232 | |
| | 1233 | z.next_in = in; |
| | 1234 | z.avail_in = in_size; |
| | 1235 | z.next_out = out; |
| | 1236 | z.avail_out = *out_size; |
| | 1237 | |
| | 1238 | ret = inflate(&z, Z_NO_FLUSH); |
| | 1239 | //inflateEnd(&z); |
| | 1240 | |
| | 1241 | *out_size -= z.avail_out; |
| | 1242 | return ret == 1 ? 0 : ret; |
| | 1243 | } |
| | 1244 | |
| | 1245 | static int cdread_compressed(FILE *f, unsigned int base, void *dest, int sector) |
| | 1246 | { |
| | 1247 | unsigned long cdbuffer_size, cdbuffer_size_expect; |
| | 1248 | unsigned int size; |
| | 1249 | int is_compressed; |
| | 1250 | off_t start_byte; |
| | 1251 | int ret, block; |
| | 1252 | |
| | 1253 | if (!cdHandle) |
| | 1254 | return -1; |
| | 1255 | if (base) |
| | 1256 | sector += base / 2352; |
| | 1257 | |
| | 1258 | block = sector >> compr_img->block_shift; |
| | 1259 | compr_img->sector_in_blk = sector & ((1 << compr_img->block_shift) - 1); |
| | 1260 | |
| | 1261 | if (block == compr_img->current_block) { |
| | 1262 | //printf("hit sect %d\n", sector); |
| | 1263 | goto finish; |
| | 1264 | } |
| | 1265 | |
| | 1266 | if (sector >= compr_img->index_len * 16) { |
| | 1267 | SysPrintf("sector %d is past img end\n", sector); |
| | 1268 | return -1; |
| | 1269 | } |
| | 1270 | |
| | 1271 | start_byte = compr_img->index_table[block] & ~OFF_T_MSB; |
| | 1272 | if (fseeko(cdHandle, start_byte, SEEK_SET) != 0) { |
| | 1273 | SysPrintf("seek error for block %d at %llx: ", |
| | 1274 | block, (long long)start_byte); |
| | 1275 | perror(NULL); |
| | 1276 | return -1; |
| | 1277 | } |
| | 1278 | |
| | 1279 | is_compressed = !(compr_img->index_table[block] & OFF_T_MSB); |
| | 1280 | size = (compr_img->index_table[block + 1] & ~OFF_T_MSB) - start_byte; |
| | 1281 | if (size > sizeof(compr_img->buff_compressed)) { |
| | 1282 | SysPrintf("block %d is too large: %u\n", block, size); |
| | 1283 | return -1; |
| | 1284 | } |
| | 1285 | |
| | 1286 | if (fread(is_compressed ? compr_img->buff_compressed : compr_img->buff_raw[0], |
| | 1287 | 1, size, cdHandle) != size) { |
| | 1288 | SysPrintf("read error for block %d at %lx: ", block, (long)start_byte); |
| | 1289 | perror(NULL); |
| | 1290 | return -1; |
| | 1291 | } |
| | 1292 | |
| | 1293 | if (is_compressed) { |
| | 1294 | cdbuffer_size_expect = sizeof(compr_img->buff_raw[0]) << compr_img->block_shift; |
| | 1295 | cdbuffer_size = cdbuffer_size_expect; |
| | 1296 | ret = uncompress2_pcsx(compr_img->buff_raw[0], &cdbuffer_size, compr_img->buff_compressed, size); |
| | 1297 | if (ret != 0) { |
| | 1298 | SysPrintf("uncompress failed with %d for block %d, sector %d\n", |
| | 1299 | ret, block, sector); |
| | 1300 | return -1; |
| | 1301 | } |
| | 1302 | if (cdbuffer_size != cdbuffer_size_expect) |
| | 1303 | SysPrintf("cdbuffer_size: %lu != %lu, sector %d\n", cdbuffer_size, |
| | 1304 | cdbuffer_size_expect, sector); |
| | 1305 | } |
| | 1306 | |
| | 1307 | // done at last! |
| | 1308 | compr_img->current_block = block; |
| | 1309 | |
| | 1310 | finish: |
| | 1311 | if (dest != NULL) |
| | 1312 | memcpy(dest, compr_img->buff_raw[compr_img->sector_in_blk], |
| | 1313 | CD_FRAMESIZE_RAW); |
| | 1314 | return CD_FRAMESIZE_RAW; |
| | 1315 | } |
| | 1316 | |
| | 1317 | #ifdef HAVE_CHD |
| | 1318 | static unsigned char *chd_get_sector(unsigned int current_buffer, unsigned int sector_in_hunk) |
| | 1319 | { |
| | 1320 | return chd_img->buffer |
| | 1321 | + current_buffer * chd_img->header->hunkbytes |
| | 1322 | + sector_in_hunk * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE); |
| | 1323 | } |
| | 1324 | |
| | 1325 | static int cdread_chd(FILE *f, unsigned int base, void *dest, int sector) |
| | 1326 | { |
| | 1327 | int hunk; |
| | 1328 | |
| | 1329 | sector += base; |
| | 1330 | |
| | 1331 | hunk = sector / chd_img->sectors_per_hunk; |
| | 1332 | chd_img->sector_in_hunk = sector % chd_img->sectors_per_hunk; |
| | 1333 | |
| | 1334 | if (hunk == chd_img->current_hunk[0]) |
| | 1335 | chd_img->current_buffer = 0; |
| | 1336 | else if (hunk == chd_img->current_hunk[1]) |
| | 1337 | chd_img->current_buffer = 1; |
| | 1338 | else |
| | 1339 | { |
| | 1340 | chd_read(chd_img->chd, hunk, chd_img->buffer + |
| | 1341 | chd_img->current_buffer * chd_img->header->hunkbytes); |
| | 1342 | chd_img->current_hunk[chd_img->current_buffer] = hunk; |
| | 1343 | } |
| | 1344 | |
| | 1345 | if (dest != NULL) |
| | 1346 | memcpy(dest, chd_get_sector(chd_img->current_buffer, chd_img->sector_in_hunk), |
| | 1347 | CD_FRAMESIZE_RAW); |
| | 1348 | return CD_FRAMESIZE_RAW; |
| | 1349 | } |
| | 1350 | |
| | 1351 | static int cdread_sub_chd(FILE *f, int sector, void *buffer_ptr) |
| | 1352 | { |
| | 1353 | unsigned int sector_in_hunk; |
| | 1354 | unsigned int buffer; |
| | 1355 | int hunk; |
| | 1356 | |
| | 1357 | if (!subChanMixed) |
| | 1358 | return -1; |
| | 1359 | |
| | 1360 | hunk = sector / chd_img->sectors_per_hunk; |
| | 1361 | sector_in_hunk = sector % chd_img->sectors_per_hunk; |
| | 1362 | |
| | 1363 | if (hunk == chd_img->current_hunk[0]) |
| | 1364 | buffer = 0; |
| | 1365 | else if (hunk == chd_img->current_hunk[1]) |
| | 1366 | buffer = 1; |
| | 1367 | else |
| | 1368 | { |
| | 1369 | buffer = chd_img->current_buffer ^ 1; |
| | 1370 | chd_read(chd_img->chd, hunk, chd_img->buffer + |
| | 1371 | buffer * chd_img->header->hunkbytes); |
| | 1372 | chd_img->current_hunk[buffer] = hunk; |
| | 1373 | } |
| | 1374 | |
| | 1375 | memcpy(buffer_ptr, chd_get_sector(buffer, sector_in_hunk) + CD_FRAMESIZE_RAW, SUB_FRAMESIZE); |
| | 1376 | return 0; |
| | 1377 | } |
| | 1378 | #endif |
| | 1379 | |
| | 1380 | static int cdread_2048(FILE *f, unsigned int base, void *dest, int sector) |
| | 1381 | { |
| | 1382 | unsigned char *dst = dest ? dest : cdbuffer; |
| | 1383 | int ret; |
| | 1384 | |
| | 1385 | if (!f) |
| | 1386 | return -1; |
| | 1387 | |
| | 1388 | fseeko(f, base + sector * 2048, SEEK_SET); |
| | 1389 | ret = fread(dst + 12 * 2, 1, 2048, f); |
| | 1390 | |
| | 1391 | // not really necessary, fake mode 2 header |
| | 1392 | memset(dst, 0, 12 * 2); |
| | 1393 | sec2msf(sector + 2 * 75, dst + 12); |
| | 1394 | dst[12 + 0] = itob(dst[12 + 0]); |
| | 1395 | dst[12 + 1] = itob(dst[12 + 1]); |
| | 1396 | dst[12 + 2] = itob(dst[12 + 2]); |
| | 1397 | dst[12 + 3] = 1; |
| | 1398 | |
| | 1399 | return 12*2 + ret; |
| | 1400 | } |
| | 1401 | |
| | 1402 | static void * ISOgetBuffer_normal(void) { |
| | 1403 | return cdbuffer + 12; |
| | 1404 | } |
| | 1405 | |
| | 1406 | static void * ISOgetBuffer_compr(void) { |
| | 1407 | return compr_img->buff_raw[compr_img->sector_in_blk] + 12; |
| | 1408 | } |
| | 1409 | |
| | 1410 | #ifdef HAVE_CHD |
| | 1411 | static void * ISOgetBuffer_chd(void) { |
| | 1412 | return chd_get_sector(chd_img->current_buffer, chd_img->sector_in_hunk) + 12; |
| | 1413 | } |
| | 1414 | #endif |
| | 1415 | |
| | 1416 | void * (*ISOgetBuffer)(void) = ISOgetBuffer_normal; |
| | 1417 | |
| | 1418 | static void PrintTracks(void) { |
| | 1419 | unsigned char msfe[3]; |
| | 1420 | int i; |
| | 1421 | |
| | 1422 | for (i = 1; i <= numtracks; i++) { |
| | 1423 | unsigned char start[3], length[3]; |
| | 1424 | sec2msf(ti[i].start, start); |
| | 1425 | sec2msf(ti[i].length, length); |
| | 1426 | SysPrintf(_("Track %.2d %s - Start %.2d:%.2d:%.2d, Length %.2d:%.2d:%.2d\n"), |
| | 1427 | i, (ti[i].type == CDRT_DATA ? "DATA " : |
| | 1428 | (ti[i].type == CDRT_CDDA ? "AUDIO" : "UNKNOWN")), |
| | 1429 | start[0], start[1], start[2], length[0], length[1], length[2]); |
| | 1430 | } |
| | 1431 | i = ti[numtracks].start + ti[numtracks].length; |
| | 1432 | sec2msf(i, msfe); |
| | 1433 | SysPrintf("End %.2d:%.2d:%.2d (sector %d)\n", msfe[0], msfe[1], msfe[2], i); |
| | 1434 | } |
| | 1435 | |
| | 1436 | // This function is invoked by the front-end when opening an ISO |
| | 1437 | // file for playback |
| | 1438 | int ISOopen(const char *fname) |
| | 1439 | { |
| | 1440 | boolean isMode1ISO = FALSE; |
| | 1441 | char alt_bin_filename[MAXPATHLEN]; |
| | 1442 | const char *bin_filename; |
| | 1443 | char image_str[1024]; |
| | 1444 | off_t size_main; |
| | 1445 | |
| | 1446 | if (cdHandle || chd_img) { |
| | 1447 | return 0; // it's already open |
| | 1448 | } |
| | 1449 | |
| | 1450 | cdHandle = fopen(fname, "rb"); |
| | 1451 | if (cdHandle == NULL) { |
| | 1452 | SysPrintf(_("Could't open '%s' for reading: %s\n"), |
| | 1453 | fname, strerror(errno)); |
| | 1454 | return -1; |
| | 1455 | } |
| | 1456 | set_static_stdio_buffer(cdHandle); |
| | 1457 | size_main = get_size(cdHandle); |
| | 1458 | |
| | 1459 | snprintf(image_str, sizeof(image_str) - 6*4 - 1, |
| | 1460 | "Loaded CD Image: %s", fname); |
| | 1461 | |
| | 1462 | cddaBigEndian = FALSE; |
| | 1463 | subChanMixed = FALSE; |
| | 1464 | subChanRaw = FALSE; |
| | 1465 | cdrIsoMultidiskCount = 1; |
| | 1466 | multifile = 0; |
| | 1467 | |
| | 1468 | ISOgetBuffer = ISOgetBuffer_normal; |
| | 1469 | cdimg_read_func = cdread_normal; |
| | 1470 | cdimg_read_sub_func = NULL; |
| | 1471 | |
| | 1472 | if (parsetoc(fname) == 0) { |
| | 1473 | strcat(image_str, "[+toc]"); |
| | 1474 | } |
| | 1475 | else if (parseccd(fname) == 0) { |
| | 1476 | strcat(image_str, "[+ccd]"); |
| | 1477 | } |
| | 1478 | else if (parsemds(fname) == 0) { |
| | 1479 | strcat(image_str, "[+mds]"); |
| | 1480 | } |
| | 1481 | else if (parsecue(fname) == 0) { |
| | 1482 | strcat(image_str, "[+cue]"); |
| | 1483 | } |
| | 1484 | if (handlepbp(fname) == 0) { |
| | 1485 | strcat(image_str, "[+pbp]"); |
| | 1486 | ISOgetBuffer = ISOgetBuffer_compr; |
| | 1487 | cdimg_read_func = cdread_compressed; |
| | 1488 | } |
| | 1489 | else if (handlecbin(fname) == 0) { |
| | 1490 | strcat(image_str, "[+cbin]"); |
| | 1491 | ISOgetBuffer = ISOgetBuffer_compr; |
| | 1492 | cdimg_read_func = cdread_compressed; |
| | 1493 | } |
| | 1494 | #ifdef HAVE_CHD |
| | 1495 | else if (handlechd(fname) == 0) { |
| | 1496 | strcat(image_str, "[+chd]"); |
| | 1497 | ISOgetBuffer = ISOgetBuffer_chd; |
| | 1498 | cdimg_read_func = cdread_chd; |
| | 1499 | cdimg_read_sub_func = cdread_sub_chd; |
| | 1500 | } |
| | 1501 | #endif |
| | 1502 | |
| | 1503 | if (!subChanMixed && opensubfile(fname) == 0) { |
| | 1504 | strcat(image_str, "[+sub]"); |
| | 1505 | } |
| | 1506 | if (opensbifile(fname) == 0) { |
| | 1507 | strcat(image_str, "[+sbi]"); |
| | 1508 | } |
| | 1509 | |
| | 1510 | // maybe user selected metadata file instead of main .bin .. |
| | 1511 | bin_filename = fname; |
| | 1512 | if (cdHandle && size_main < 2352 * 0x10) { |
| | 1513 | static const char *exts[] = { ".bin", ".BIN", ".img", ".IMG" }; |
| | 1514 | FILE *tmpf = NULL; |
| | 1515 | size_t i; |
| | 1516 | char *p; |
| | 1517 | |
| | 1518 | strncpy(alt_bin_filename, bin_filename, sizeof(alt_bin_filename)); |
| | 1519 | alt_bin_filename[MAXPATHLEN - 1] = '\0'; |
| | 1520 | if (strlen(alt_bin_filename) >= 4) { |
| | 1521 | p = alt_bin_filename + strlen(alt_bin_filename) - 4; |
| | 1522 | for (i = 0; i < sizeof(exts) / sizeof(exts[0]); i++) { |
| | 1523 | strcpy(p, exts[i]); |
| | 1524 | tmpf = fopen(alt_bin_filename, "rb"); |
| | 1525 | if (tmpf != NULL) |
| | 1526 | break; |
| | 1527 | } |
| | 1528 | } |
| | 1529 | if (tmpf != NULL) { |
| | 1530 | bin_filename = alt_bin_filename; |
| | 1531 | fclose(cdHandle); |
| | 1532 | cdHandle = tmpf; |
| | 1533 | set_static_stdio_buffer(cdHandle); |
| | 1534 | size_main = get_size(cdHandle); |
| | 1535 | } |
| | 1536 | } |
| | 1537 | |
| | 1538 | // guess whether it is mode1/2048 |
| | 1539 | if (cdHandle && cdimg_read_func == cdread_normal && size_main % 2048 == 0) { |
| | 1540 | unsigned int modeTest = 0; |
| | 1541 | if (!fread(&modeTest, sizeof(modeTest), 1, cdHandle)) { |
| | 1542 | SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); |
| | 1543 | } |
| | 1544 | if (SWAP32(modeTest) != 0xffffff00) { |
| | 1545 | strcat(image_str, "[2048]"); |
| | 1546 | isMode1ISO = TRUE; |
| | 1547 | } |
| | 1548 | } |
| | 1549 | if (cdHandle && numtracks == 0) { |
| | 1550 | // assume some metadata-less format |
| | 1551 | numtracks = 1; |
| | 1552 | ti[1].type = CDRT_DATA; |
| | 1553 | ti[1].start_offset = 0; |
| | 1554 | ti[1].start = 2 * 75; |
| | 1555 | ti[1].length = isMode1ISO ? size_main / 2048u : size_main / 2352u; |
| | 1556 | } |
| | 1557 | |
| | 1558 | SysPrintf("%s (%lld bytes).\n", image_str, (long long)size_main); |
| | 1559 | |
| | 1560 | PrintTracks(); |
| | 1561 | |
| | 1562 | if (subChanMixed && cdimg_read_func == cdread_normal) { |
| | 1563 | cdimg_read_func = cdread_sub_mixed; |
| | 1564 | cdimg_read_sub_func = cdread_sub_sub_mixed; |
| | 1565 | } |
| | 1566 | else if (isMode1ISO) { |
| | 1567 | cdimg_read_func = cdread_2048; |
| | 1568 | cdimg_read_sub_func = NULL; |
| | 1569 | } |
| | 1570 | |
| | 1571 | return 0; |
| | 1572 | } |
| | 1573 | |
| | 1574 | int ISOclose(void) |
| | 1575 | { |
| | 1576 | int i; |
| | 1577 | |
| | 1578 | if (cdHandle != NULL) { |
| | 1579 | fclose(cdHandle); |
| | 1580 | cdHandle = NULL; |
| | 1581 | } |
| | 1582 | if (subHandle != NULL) { |
| | 1583 | fclose(subHandle); |
| | 1584 | subHandle = NULL; |
| | 1585 | } |
| | 1586 | |
| | 1587 | if (compr_img != NULL) { |
| | 1588 | free(compr_img->index_table); |
| | 1589 | free(compr_img); |
| | 1590 | compr_img = NULL; |
| | 1591 | } |
| | 1592 | |
| | 1593 | #ifdef HAVE_CHD |
| | 1594 | if (chd_img != NULL) { |
| | 1595 | chd_close(chd_img->chd); |
| | 1596 | free(chd_img->buffer); |
| | 1597 | free(chd_img); |
| | 1598 | chd_img = NULL; |
| | 1599 | } |
| | 1600 | #endif |
| | 1601 | |
| | 1602 | for (i = 1; i <= numtracks; i++) { |
| | 1603 | if (ti[i].handle != NULL) { |
| | 1604 | fclose(ti[i].handle); |
| | 1605 | ti[i].handle = NULL; |
| | 1606 | } |
| | 1607 | } |
| | 1608 | numtracks = 0; |
| | 1609 | ti[1].type = CDRT_UNKNOWN; |
| | 1610 | UnloadSBI(); |
| | 1611 | |
| | 1612 | memset(cdbuffer, 0, sizeof(cdbuffer)); |
| | 1613 | ISOgetBuffer = ISOgetBuffer_normal; |
| | 1614 | |
| | 1615 | return 0; |
| | 1616 | } |
| | 1617 | |
| | 1618 | int ISOinit(void) |
| | 1619 | { |
| | 1620 | assert(cdHandle == NULL); |
| | 1621 | assert(subHandle == NULL); |
| | 1622 | numtracks = 0; |
| | 1623 | |
| | 1624 | return 0; // do nothing |
| | 1625 | } |
| | 1626 | |
| | 1627 | int ISOshutdown(void) |
| | 1628 | { |
| | 1629 | return ISOclose(); |
| | 1630 | } |
| | 1631 | |
| | 1632 | // return Starting and Ending Track |
| | 1633 | // buffer: |
| | 1634 | // byte 0 - start track |
| | 1635 | // byte 1 - end track |
| | 1636 | int ISOgetTN(unsigned char *buffer) |
| | 1637 | { |
| | 1638 | buffer[0] = 1; |
| | 1639 | |
| | 1640 | if (numtracks > 0) { |
| | 1641 | buffer[1] = numtracks; |
| | 1642 | } |
| | 1643 | else { |
| | 1644 | buffer[1] = 1; |
| | 1645 | } |
| | 1646 | |
| | 1647 | return 0; |
| | 1648 | } |
| | 1649 | |
| | 1650 | // return Track Time |
| | 1651 | // buffer: |
| | 1652 | // byte 0 - minute |
| | 1653 | // byte 1 - second |
| | 1654 | // byte 2 - frame |
| | 1655 | int ISOgetTD(int track, unsigned char *buffer) |
| | 1656 | { |
| | 1657 | if (track == 0) { |
| | 1658 | sec2msf(ti[numtracks].start + ti[numtracks].length, buffer); |
| | 1659 | } |
| | 1660 | else if (numtracks > 0 && track <= numtracks) { |
| | 1661 | sec2msf(ti[track].start, buffer); |
| | 1662 | } |
| | 1663 | else { |
| | 1664 | buffer[2] = 0; |
| | 1665 | buffer[1] = 2; |
| | 1666 | buffer[0] = 0; |
| | 1667 | } |
| | 1668 | |
| | 1669 | return 0; |
| | 1670 | } |
| | 1671 | |
| | 1672 | // decode 'raw' subchannel data ripped by cdrdao |
| | 1673 | static void DecodeRawSubData(unsigned char *subbuffer) { |
| | 1674 | unsigned char subQData[12]; |
| | 1675 | int i; |
| | 1676 | |
| | 1677 | memset(subQData, 0, sizeof(subQData)); |
| | 1678 | |
| | 1679 | for (i = 0; i < 8 * 12; i++) { |
| | 1680 | if (subbuffer[i] & (1 << 6)) { // only subchannel Q is needed |
| | 1681 | subQData[i >> 3] |= (1 << (7 - (i & 7))); |
| | 1682 | } |
| | 1683 | } |
| | 1684 | |
| | 1685 | memcpy(&subbuffer[12], subQData, 12); |
| | 1686 | } |
| | 1687 | |
| | 1688 | // read track |
| | 1689 | // time: byte 0 - minute; byte 1 - second; byte 2 - frame (non-bcd) |
| | 1690 | // buf: if NULL, data is kept in internal buffer accessible by ISOgetBuffer() |
| | 1691 | int ISOreadTrack(const unsigned char *time, void *buf) |
| | 1692 | { |
| | 1693 | int sector = msf2sec(time); |
| | 1694 | long ret; |
| | 1695 | |
| | 1696 | if (!cdHandle && !chd_img) |
| | 1697 | return -1; |
| | 1698 | |
| | 1699 | if (sector >= ti[1].start + ti[1].length && |
| | 1700 | numtracks > 1 && ti[2].type == CDRT_CDDA) { |
| | 1701 | return ISOreadCDDA(time, buf); |
| | 1702 | } |
| | 1703 | |
| | 1704 | sector -= 2 * 75; |
| | 1705 | |
| | 1706 | ret = cdimg_read_func(cdHandle, 0, buf, sector); |
| | 1707 | if (ret < 12*2 + 2048) |
| | 1708 | return -1; |
| | 1709 | |
| | 1710 | return 0; |
| | 1711 | } |
| | 1712 | |
| | 1713 | // read subchannel data |
| | 1714 | int ISOreadSub(const unsigned char *time, void *buffer) |
| | 1715 | { |
| | 1716 | int ret, sector = msf2sec(time); |
| | 1717 | |
| | 1718 | if (sector >= ti[1].start + ti[1].length) { |
| | 1719 | // for tracks 2+ use the fake data, otherwise |
| | 1720 | // it becomes too messy to handle all the gap stuff |
| | 1721 | return -1; |
| | 1722 | } |
| | 1723 | sector -= 2 * 75; |
| | 1724 | |
| | 1725 | if (cdimg_read_sub_func != NULL) { |
| | 1726 | if ((ret = cdimg_read_sub_func(cdHandle, sector, buffer))) |
| | 1727 | return ret; |
| | 1728 | } |
| | 1729 | else if (subHandle != NULL) { |
| | 1730 | if (fseeko(subHandle, sector * SUB_FRAMESIZE, SEEK_SET)) |
| | 1731 | return -1; |
| | 1732 | if (fread(buffer, 1, SUB_FRAMESIZE, subHandle) != SUB_FRAMESIZE) |
| | 1733 | return -1; |
| | 1734 | } |
| | 1735 | else { |
| | 1736 | return -1; |
| | 1737 | } |
| | 1738 | |
| | 1739 | if (subChanRaw) |
| | 1740 | DecodeRawSubData(buffer); |
| | 1741 | return 0; |
| | 1742 | } |
| | 1743 | |
| | 1744 | int ISOgetStatus(struct CdrStat *stat) |
| | 1745 | { |
| | 1746 | CDR__getStatus(stat); |
| | 1747 | |
| | 1748 | // BIOS - boot ID (CD type) |
| | 1749 | stat->Type = ti[1].type; |
| | 1750 | |
| | 1751 | return 0; |
| | 1752 | } |
| | 1753 | |
| | 1754 | // read CDDA sector into buffer |
| | 1755 | int ISOreadCDDA(const unsigned char *time, void *buffer) |
| | 1756 | { |
| | 1757 | unsigned int track, track_start = 0; |
| | 1758 | FILE *handle = cdHandle; |
| | 1759 | unsigned int cddaCurPos; |
| | 1760 | int ret, ret_clear = -1; |
| | 1761 | |
| | 1762 | cddaCurPos = msf2sec(time); |
| | 1763 | |
| | 1764 | // find current track index |
| | 1765 | for (track = numtracks; ; track--) { |
| | 1766 | track_start = ti[track].start; |
| | 1767 | if (track_start <= cddaCurPos) |
| | 1768 | break; |
| | 1769 | if (track == 1) |
| | 1770 | break; |
| | 1771 | } |
| | 1772 | |
| | 1773 | if (track == numtracks && cddaCurPos >= ti[track].start + ti[track].length) |
| | 1774 | return -1; |
| | 1775 | if (ti[track].type != CDRT_CDDA) { |
| | 1776 | // data tracks play silent |
| | 1777 | ret_clear = 0; |
| | 1778 | goto clear_return; |
| | 1779 | } |
| | 1780 | if (track < numtracks && cddaCurPos >= ti[track].start + ti[track].length) { |
| | 1781 | // gap |
| | 1782 | ret_clear = 0; |
| | 1783 | goto clear_return; |
| | 1784 | } |
| | 1785 | |
| | 1786 | if (multifile) { |
| | 1787 | // find the file that contains this track |
| | 1788 | unsigned int file; |
| | 1789 | for (file = track; file > 1; file--) { |
| | 1790 | if (ti[file].handle != NULL) { |
| | 1791 | handle = ti[file].handle; |
| | 1792 | break; |
| | 1793 | } |
| | 1794 | } |
| | 1795 | } |
| | 1796 | if (!handle && !chd_img) |
| | 1797 | goto clear_return; |
| | 1798 | |
| | 1799 | ret = cdimg_read_func(handle, ti[track].start_offset, |
| | 1800 | buffer, cddaCurPos - track_start); |
| | 1801 | if (ret != CD_FRAMESIZE_RAW) |
| | 1802 | goto clear_return; |
| | 1803 | |
| | 1804 | if (cddaBigEndian && buffer) { |
| | 1805 | unsigned char tmp, *buf = buffer; |
| | 1806 | int i; |
| | 1807 | |
| | 1808 | for (i = 0; i < CD_FRAMESIZE_RAW / 2; i++) { |
| | 1809 | tmp = buf[i * 2]; |
| | 1810 | buf[i * 2] = buf[i * 2 + 1]; |
| | 1811 | buf[i * 2 + 1] = tmp; |
| | 1812 | } |
| | 1813 | } |
| | 1814 | |
| | 1815 | return 0; |
| | 1816 | |
| | 1817 | clear_return: |
| | 1818 | if (buffer) |
| | 1819 | memset(buffer, 0, CD_FRAMESIZE_RAW); |
| | 1820 | return ret_clear; |
| | 1821 | } |