bool CDAccess_Image::ImageOpen()

in cores/pce/mednafen/cdrom/CDAccess_Image.cpp [384:993]


bool CDAccess_Image::ImageOpen(const std::string& path, bool image_memcache)
{
   MemoryStream fp(new FileStream(path.c_str(), MODE_READ));
   static const unsigned max_args = 4;
   std::string linebuf;
   std::string cmdbuf, args[max_args];
   bool IsTOC = false;
   int32_t active_track = -1;
   int32_t AutoTrackInc = 1; // For TOC
   CDRFILE_TRACK_INFO TmpTrack;
   std::string file_base, file_ext;
   std::map<std::string, Stream*> toc_streamcache;

   disc_type = DISC_TYPE_CDDA_OR_M1;
   memset(&TmpTrack, 0, sizeof(TmpTrack));

   MDFN_GetFilePathComponents(path, &base_dir, &file_base, &file_ext);

   if(!strcasecmp(file_ext.c_str(), ".toc"))
   {
      log_cb(RETRO_LOG_INFO, "TOC file detected.\n");
      IsTOC = true;
   }

   // Check for annoying UTF-8 BOM.
   if(!IsTOC)
   {
      uint8_t bom_tmp[3];

      if(fp.read(bom_tmp, 3, false) == 3 && bom_tmp[0] == 0xEF && bom_tmp[1] == 0xBB && bom_tmp[2] == 0xBF)
      {
         // Print an annoying error message, but don't actually error out.
         log_cb(RETRO_LOG_WARN, "UTF-8 BOM detected at start of CUE sheet.\n");
      }
      else
         fp.seek(0, SEEK_SET);
   }


   // Assign opposite maximum values so our tests will work!
   FirstTrack = 99;
   LastTrack = 0;

   linebuf.reserve(1024);
   while(fp.get_line(linebuf) >= 0)
   {
      unsigned argcount = 0;

      if(IsTOC)
      {
         // Handle TOC format comments
         size_t ss_loc = linebuf.find("//");

         if(ss_loc != std::string::npos)
            linebuf.resize(ss_loc);
      }

      // Call trim AFTER we handle TOC-style comments, so we'll be sure to remove trailing whitespace in lines like: MONKEY  // BABIES
      MDFN_rtrim(linebuf);
      MDFN_ltrim(linebuf);

      if(linebuf.length() == 0)	// Skip blank lines.
         continue;

      // Grab command and arguments.
      {
         size_t offs = 0;

         offs = UnQuotify(linebuf, offs, cmdbuf, false);
         for(argcount = 0; argcount < max_args && offs < linebuf.length(); argcount++)
            offs = UnQuotify(linebuf, offs, args[argcount]);

         // Make sure unused arguments are cleared out so we don't have inter-line leaks!
         for(unsigned x = argcount; x < max_args; x++)
            args[x].clear();

         MDFN_strtoupper(cmdbuf);
      }

      //printf("%s\n", cmdbuf.c_str()); //: %s %s %s %s\n", cmdbuf.c_str(), args[0].c_str(), args[1].c_str(), args[2].c_str(), args[3].c_str());

      if(IsTOC)
      {
         if(cmdbuf == "TRACK")
         {
            if(active_track >= 0)
            {
               memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
               memset(&TmpTrack, 0, sizeof(TmpTrack));
               active_track = -1;
            }

            for(int32_t i = 2; i < 100; i++)
               TmpTrack.index[i] = -1;

            if(AutoTrackInc > 99)
            {
               log_cb(RETRO_LOG_ERROR, "Invalid track number: %d", AutoTrackInc);
               return false;
            }

            active_track = AutoTrackInc++;
            if(active_track < FirstTrack)
               FirstTrack = active_track;
            if(active_track > LastTrack)
               LastTrack = active_track;

            int format_lookup;
            for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
            {
               if(!strcasecmp(args[0].c_str(), DI_CDRDAO_Strings[format_lookup]))
               {
                  TmpTrack.DIFormat = format_lookup;
                  break;
               }
            }

            if(format_lookup == _DI_FORMAT_COUNT)
            {
               log_cb(RETRO_LOG_ERROR, "Invalid track format: %s", args[0].c_str());
               return false;
            }

            if(TmpTrack.DIFormat == DI_FORMAT_AUDIO)
               TmpTrack.RawAudioMSBFirst = true; /* Silly cdrdao... */

            if(!strcasecmp(args[1].c_str(), "RW"))
            {
               TmpTrack.SubchannelMode = CDRF_SUBM_RW;
               log_cb(RETRO_LOG_ERROR, "\"RW\" format subchannel data not supported, only \"RW_RAW\" is!");
            }
            else if(!strcasecmp(args[1].c_str(), "RW_RAW"))
               TmpTrack.SubchannelMode = CDRF_SUBM_RW_RAW;

         } // end to TRACK
         else if(cmdbuf == "SILENCE")
         {
#if 0
            log_cb(RETRO_LOG_INFO, "Unsupported directive: %s\n", cmdbuf.c_str());
            return false;
#endif
         }
         else if(cmdbuf == "ZERO")
         {
#if 0
            log_cb(RETRO_LOG_INFO, "Unsupported directive: %s\n", cmdbuf.c_str());
            return false;
#endif
         }
         else if(cmdbuf == "FIFO")
         {
            log_cb(RETRO_LOG_INFO, "Unsupported directive: %s\n", cmdbuf.c_str());
            return false;
         }
         else if(cmdbuf == "FILE" || cmdbuf == "AUDIOFILE")
         {
            const char *binoffset = NULL;
            const char *msfoffset = NULL;
            const char *length = NULL;

            if(args[1].c_str()[0] == '#')
            {
               binoffset = args[1].c_str() + 1;
               msfoffset = args[2].c_str();
               length = args[3].c_str();
            }
            else
            {
               msfoffset = args[1].c_str();
               length = args[2].c_str();
            }
            //printf("%s, %s, %s, %s\n", args[0].c_str(), binoffset, msfoffset, length);
            if (!ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length, image_memcache, toc_streamcache))
               return false;
         }
         else if(cmdbuf == "DATAFILE")
         {
            const char *binoffset = NULL;
            const char *length = NULL;

            if(args[1].c_str()[0] == '#') 
            {
               binoffset = args[1].c_str() + 1;
               length = args[2].c_str();
            }
            else
               length = args[1].c_str();

            if (!ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length, image_memcache, toc_streamcache))
               return false;
         }
         else if(cmdbuf == "INDEX")
         {
            // FIXME
            log_cb(RETRO_LOG_ERROR, "Unsupported directive: %s", cmdbuf.c_str());
            return false;
         }
         else if(cmdbuf == "PREGAP")
         {
            if(active_track < 0)
            {
               log_cb(RETRO_LOG_ERROR, "Command %s is outside of a TRACK definition!\n", cmdbuf.c_str());
               return false;
            }

            unsigned int m,s,f;

            if (!StringToMSF(args[0].c_str(), &m, &s, &f))
               return false;

            TmpTrack.pregap = (m * 60 + s) * 75 + f;
         } // end to PREGAP
         else if(cmdbuf == "START")
         {
            if(active_track < 0)
            {
               log_cb(RETRO_LOG_ERROR, "Command %s is outside of a TRACK definition!\n", cmdbuf.c_str());
               return false;
            }

            unsigned int m,s,f;

            if (!StringToMSF(args[0].c_str(), &m, &s, &f))
               return false;

            TmpTrack.pregap = (m * 60 + s) * 75 + f;
         }
         else if(cmdbuf == "TWO_CHANNEL_AUDIO")
         {
            TmpTrack.subq_control &= ~SUBQ_CTRLF_4CH;
         }
         else if(cmdbuf == "FOUR_CHANNEL_AUDIO")
         {
            TmpTrack.subq_control |= SUBQ_CTRLF_4CH;
         }
         else if(cmdbuf == "NO")
         {
            MDFN_strtoupper(args[0]);

            if(args[0] == "COPY")
            {
               TmpTrack.subq_control &= ~SUBQ_CTRLF_DCP; 
            }
            else if(args[0] == "PRE_EMPHASIS")
            {
               TmpTrack.subq_control &= ~SUBQ_CTRLF_PRE;
            }
            else
            {
               log_cb(RETRO_LOG_ERROR, "Unsupported argument to \"NO\" directive: %s", args[0].c_str());
               return false;
            }
         }
         else if(cmdbuf == "COPY")
         {
            TmpTrack.subq_control |= SUBQ_CTRLF_DCP;
         } 
         else if(cmdbuf == "PRE_EMPHASIS")
         {
            TmpTrack.subq_control |= SUBQ_CTRLF_PRE;
         }
         // TODO: Confirm that these are taken from the TOC of the disc, and not synthesized by cdrdao.
         else if(cmdbuf == "CD_DA")
            disc_type = DISC_TYPE_CDDA_OR_M1;
         else if(cmdbuf == "CD_ROM")
            disc_type = DISC_TYPE_CDDA_OR_M1;
         else if(cmdbuf == "CD_ROM_XA")
            disc_type = DISC_TYPE_CD_XA;
         else
         {
#if 0
            log_cb(RETRO_LOG_ERROR, "Unsupported directive: %s", cmdbuf.c_str());
            return false;
#endif
         }
         // TODO: CATALOG

      } /*********** END TOC HANDLING ************/
      else // now for CUE sheet handling
      {
         if(cmdbuf == "FILE")
         {
            if(active_track >= 0)
            {
               memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
               memset(&TmpTrack, 0, sizeof(TmpTrack));
               active_track = -1;
            }

            std::string efn = MDFN_EvalFIP(base_dir, args[0]);
            TmpTrack.fp = new FileStream(efn.c_str(), MODE_READ);
            TmpTrack.FirstFileInstance = 1;

            if(image_memcache)
               TmpTrack.fp = new MemoryStream(TmpTrack.fp);

            if(!strcasecmp(args[1].c_str(), "BINARY"))
            {
               //TmpTrack.Format = TRACK_FORMAT_DATA;
               //struct stat stat_buf;
               //fstat(fileno(TmpTrack.fp), &stat_buf);
               //TmpTrack.sectors = stat_buf.st_size; // / 2048;
            }
            else if(!strcasecmp(args[1].c_str(), "WAVE") || !strcasecmp(args[1].c_str(), "WAV"))
            {
               // Make it work with WAVE / WAV file type names in the cue sheet, previously .wav was working only with BINARY
            }
            else if(!strcasecmp(args[1].c_str(), "OGG") || !strcasecmp(args[1].c_str(), "VORBIS") || !strcasecmp(args[1].c_str(), "PCM")
                  || !strcasecmp(args[1].c_str(), "MPC") || !strcasecmp(args[1].c_str(), "MP+"))
            {
               TmpTrack.AReader = CDAFR_Open(TmpTrack.fp);
               if(!TmpTrack.AReader)
               {
                  log_cb(RETRO_LOG_ERROR, "Unsupported audio track file format: %s\n", args[0].c_str());
                  return false;
               }
            }
            else
            {
               log_cb(RETRO_LOG_ERROR, "Unsupported track format: %s\n", args[1].c_str());
               return false;
            }
         }
         else if(cmdbuf == "TRACK")
         {
            if(active_track >= 0)
            {
               memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
               TmpTrack.FirstFileInstance = 0;
               TmpTrack.pregap = 0;
               TmpTrack.pregap_dv = 0;
               TmpTrack.postgap = 0;
               TmpTrack.index[0] = -1;
               TmpTrack.index[1] = 0;
            }

            for(int32_t i = 2; i < 100; i++)
               TmpTrack.index[i] = -1;

            active_track = atoi(args[0].c_str());

            if(active_track < 1 || active_track > 99)
            {
               log_cb(RETRO_LOG_ERROR, "Invalid track number: %d\n", active_track);
               return false;
            }

            if(active_track < FirstTrack)
               FirstTrack = active_track;
            if(active_track > LastTrack)
               LastTrack = active_track;

            int format_lookup;
            for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
            {
               if(!strcasecmp(args[1].c_str(), DI_CUE_Strings[format_lookup]))
               {
                  TmpTrack.DIFormat = format_lookup;
                  break;
               }
            }

            if(format_lookup == _DI_FORMAT_COUNT)
            {
               log_cb(RETRO_LOG_ERROR, "Invalid track format: %s\n", args[1].c_str());
               return false;
            }
         }
         else if(cmdbuf == "INDEX")
         {
            if(active_track >= 0)
            {
               unsigned wi;
               unsigned int m,s,f;

               if (!StringToMSF(args[1].c_str(), &m, &s, &f))
                  return false;

               if(sscanf(args[0].c_str(), "%u", &wi) == 1 && wi < 100)
                  TmpTrack.index[wi] = (m * 60 + s) * 75 + f;
               else
               {
                  log_cb(RETRO_LOG_ERROR, "Malformed \"INDEX\" directive: %s\n", cmdbuf.c_str());
                  return false;
               }
            }
         }
         else if(cmdbuf == "PREGAP")
         {
            if(active_track >= 0)
            {
               unsigned int m,s,f;

               if (!StringToMSF(args[0].c_str(), &m, &s, &f))
                  return false;

               TmpTrack.pregap = (m * 60 + s) * 75 + f;
            }
         }
         else if(cmdbuf == "POSTGAP")
         {
            if(active_track >= 0)
            {
               unsigned int m,s,f;

               if (!StringToMSF(args[0].c_str(), &m, &s, &f))
                  return false;

               TmpTrack.postgap = (m * 60 + s) * 75 + f;
            }
         }
         else if(cmdbuf == "REM")
         {

         }
         else if(cmdbuf == "FLAGS")
         {
            TmpTrack.subq_control &= ~(SUBQ_CTRLF_PRE | SUBQ_CTRLF_DCP | SUBQ_CTRLF_4CH);
            for(unsigned i = 0; i < argcount; i++)
            {
               if(args[i] == "DCP")
               {
                  TmpTrack.subq_control |= SUBQ_CTRLF_DCP;
               }
               else if(args[i] == "4CH")
               {
                  TmpTrack.subq_control |= SUBQ_CTRLF_4CH;
               }
               else if(args[i] == "PRE")
               {
                  TmpTrack.subq_control |= SUBQ_CTRLF_PRE;
               }
               else if(args[i] == "SCMS")
               {
                  // Not implemented, likely pointless.  PROBABLY indicates that the copy bit of the subchannel Q control field is supposed to
                  // alternate between 1 and 0 at 9.375 Hz(four 1, four 0, four 1, four 0, etc.).
               }
               else
               {
                  log_cb(RETRO_LOG_ERROR, "Unknown CUE sheet \"FLAGS\" directive flag \"%s\".\n", args[i].c_str());
                  return false;
               }
            }
         }
         else if(cmdbuf == "CDTEXTFILE" || cmdbuf == "CATALOG" || cmdbuf == "ISRC" ||
               cmdbuf == "TITLE" || cmdbuf == "PERFORMER" || cmdbuf == "SONGWRITER")
         {
            log_cb(RETRO_LOG_WARN, "Unsupported CUE sheet directive: \"%s\".\n", cmdbuf.c_str());	/* FIXME, generic logger passed by pointer to constructor */
         }
         else
         {
            log_cb(RETRO_LOG_ERROR, "Unknown CUE sheet directive \"%s\".\n", cmdbuf.c_str());
            return false;
         }
      } // end of CUE sheet handling
   } // end of fgets() loop

   if(active_track >= 0)
      memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));

   if(FirstTrack > LastTrack)
   {
      log_cb(RETRO_LOG_ERROR, "No tracks found!\n");
      return false;
   }

   FirstTrack = FirstTrack;
   NumTracks = 1 + LastTrack - FirstTrack;

   int32_t RunningLBA = 0;
   int32_t LastIndex = 0;
   long FileOffset = 0;

   RunningLBA -= 150;
   Tracks[FirstTrack].pregap += 150;

   for(int x = FirstTrack; x < (FirstTrack + NumTracks); x++)
   {
      if(!Tracks[x].fp && !Tracks[x].AReader)
      {
         log_cb(RETRO_LOG_ERROR, "Missing track %u.\n", x);
         return false;
      }

      if(Tracks[x].DIFormat == DI_FORMAT_AUDIO)
         Tracks[x].subq_control &= ~SUBQ_CTRLF_DATA;
      else
         Tracks[x].subq_control |= SUBQ_CTRLF_DATA;

      if(!IsTOC)	// TOC-format disc_type calculation is handled differently.
      {
         if(disc_type != DISC_TYPE_CD_I)
         {
            switch(Tracks[x].DIFormat)
            {
               default: break;

               case DI_FORMAT_MODE2:
               case DI_FORMAT_MODE2_FORM1:
               case DI_FORMAT_MODE2_FORM2:
               case DI_FORMAT_MODE2_RAW:
                        disc_type = DISC_TYPE_CD_XA;	
                        break;

               case DI_FORMAT_CDI_RAW:
                        disc_type = DISC_TYPE_CD_I;
                        break;
            }
         }
      }

      if(IsTOC)
      {
         RunningLBA += Tracks[x].pregap;
         Tracks[x].LBA = RunningLBA;
         RunningLBA += Tracks[x].sectors;
         RunningLBA += Tracks[x].postgap;
      }
      else // else handle CUE sheet...
      {
         if(Tracks[x].FirstFileInstance) 
         {
            LastIndex = 0;
            FileOffset = 0;
         }

         RunningLBA += Tracks[x].pregap;

         Tracks[x].pregap_dv = 0;

         if(Tracks[x].index[0] != -1)
            Tracks[x].pregap_dv = Tracks[x].index[1] - Tracks[x].index[0];

         FileOffset += Tracks[x].pregap_dv * DI_Size_Table[Tracks[x].DIFormat];

         RunningLBA += Tracks[x].pregap_dv;

         Tracks[x].LBA = RunningLBA;

         // Make sure FileOffset this is set before the call to GetSectorCount()
         Tracks[x].FileOffset = FileOffset;
         Tracks[x].sectors = GetSectorCount(&Tracks[x]);

         if((x + 1) >= (FirstTrack + NumTracks) || Tracks[x+1].FirstFileInstance)
         {

         }
         else
         { 
            // Fix the sector count if we have multiple tracks per one binary image file.
            if(Tracks[x + 1].index[0] == -1)
               Tracks[x].sectors = Tracks[x + 1].index[1] - Tracks[x].index[1];
            else
               Tracks[x].sectors = Tracks[x + 1].index[0] - Tracks[x].index[1];	//Tracks[x + 1].index - Tracks[x].index;
         }

         //printf("Poo: %d %d\n", x, Tracks[x].sectors);
         RunningLBA += Tracks[x].sectors;
         RunningLBA += Tracks[x].postgap;

         //printf("%d, %ld %d %d %d %d\n", x, FileOffset, Tracks[x].index, Tracks[x].pregap, Tracks[x].sectors, Tracks[x].LBA);

         FileOffset += Tracks[x].sectors * DI_Size_Table[Tracks[x].DIFormat];
      } // end to cue sheet handling
   } // end to track loop

   total_sectors = RunningLBA;

   //
   // Adjust indexes for MakeSubPQ()
   //
   for(int x = FirstTrack; x < (FirstTrack + NumTracks); x++)
   {
      const int32_t base = Tracks[x].index[1];

      for(int32_t i = 0; i < 100; i++)
      {
         if(i == 0 || Tracks[x].index[i] == -1)
            Tracks[x].index[i] = INT32_MAX;
         else
            Tracks[x].index[i] = Tracks[x].LBA + (Tracks[x].index[i] - base);

         assert(Tracks[x].index[i] >= 0);
      }
   }

   //
   // Load SBI file, if present
   //
   if(!IsTOC)
   {
      char sbi_ext[4] = { 's', 'b', 'i', 0 };

      if(file_ext.length() == 4 && file_ext[0] == '.')
      {
         for(unsigned i = 0; i < 3; i++)
         {
            if(file_ext[1 + i] >= 'A' && file_ext[1 + i] <= 'Z')
               sbi_ext[i] += 'A' - 'a';
         }
      }

      if (!LoadSBI(MDFN_EvalFIP(base_dir, file_base + std::string(".") + std::string(sbi_ext), true).c_str()))
         return false;
   }

   GenerateTOC();

   return true;
}