ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

macosx-audiocd.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  * Original license header:
00022  *
00023  * Cabal - Legacy Game Implementations
00024  *
00025  * Cabal is the legal property of its developers, whose names
00026  * are too numerous to list here. Please refer to the COPYRIGHT
00027  * file distributed with this source distribution.
00028  *
00029  * This program is free software; you can redistribute it and/or
00030  * modify it under the terms of the GNU General Public License
00031  * as published by the Free Software Foundation; either version 2
00032  * of the License, or (at your option) any later version.
00033  *
00034  * This program is distributed in the hope that it will be useful,
00035  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00036  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00037  * GNU General Public License for more details.
00038  *
00039  * You should have received a copy of the GNU General Public License
00040  * along with this program; if not, write to the Free Software
00041  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00042  *
00043  */
00044 
00045 #ifdef MACOSX
00046 
00047 #include <sys/stat.h>
00048 #include <sys/mount.h>
00049 #include <limits.h>
00050 
00051 #include "common/scummsys.h"
00052 
00053 #include "audio/audiostream.h"
00054 #include "audio/decoders/aiff.h"
00055 #include "audio/timestamp.h"
00056 #include "common/config-manager.h"
00057 #include "common/debug.h"
00058 #include "common/fs.h"
00059 #include "common/hashmap.h"
00060 #include "common/textconsole.h"
00061 #include "backends/audiocd/default/default-audiocd.h"
00062 #include "backends/audiocd/macosx/macosx-audiocd.h"
00063 #include "backends/fs/stdiostream.h"
00064 
00065 // Partially based on SDL's code
00066 
00070 class MacOSXAudioCDManager : public DefaultAudioCDManager {
00071 public:
00072     MacOSXAudioCDManager() {}
00073     ~MacOSXAudioCDManager();
00074 
00075     bool open() override;
00076     void close() override;
00077     bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
00078             Audio::Mixer::SoundType soundType) override;
00079 
00080 protected:
00081     bool openCD(int drive) override;
00082     bool openCD(const Common::String &drive) override;
00083 
00084 private:
00085     struct Drive {
00086         Drive(const Common::String &m, const Common::String &d, const Common::String &f) :
00087             mountPoint(m), deviceName(d), fsType(f) {}
00088 
00089         Common::String mountPoint;
00090         Common::String deviceName;
00091         Common::String fsType;
00092     };
00093 
00094     typedef Common::Array<Drive> DriveList;
00095     DriveList detectAllDrives();
00096     DriveList detectCDDADrives();
00097 
00098     bool findTrackNames(const Common::String &drivePath);
00099 
00100     Common::HashMap<uint, Common::String> _trackMap;
00101 };
00102 
00103 MacOSXAudioCDManager::~MacOSXAudioCDManager() {
00104     close();
00105 }
00106 
00107 bool MacOSXAudioCDManager::open() {
00108     close();
00109 
00110     if (openRealCD())
00111         return true;
00112 
00113     return DefaultAudioCDManager::open();
00114 }
00115 
00120 static int findBaseDiskNumber(const Common::String &diskName) {
00121     if (!diskName.hasPrefix("/dev/disk"))
00122         return -1;
00123 
00124     const char *startPtr = diskName.c_str() + 9;
00125     char *endPtr;
00126     int baseDiskNumber = strtol(startPtr, &endPtr, 10);
00127     if (startPtr == endPtr)
00128         return -1;
00129 
00130     return baseDiskNumber;
00131 }
00132 
00133 bool MacOSXAudioCDManager::openCD(int drive) {
00134     DriveList allDrives = detectAllDrives();
00135     if (allDrives.empty())
00136         return false;
00137 
00138     DriveList cddaDrives;
00139 
00140     // Try to get the volume related to the game's path
00141     if (ConfMan.hasKey("path")) {
00142         Common::String gamePath = ConfMan.get("path");
00143         struct statfs gamePathStat;
00144         if (statfs(gamePath.c_str(), &gamePathStat) == 0) {
00145             int baseDiskNumber = findBaseDiskNumber(gamePathStat.f_mntfromname);
00146             if (baseDiskNumber >= 0) {
00147                 // Look for a CDDA drive with the same base disk number
00148                 for (uint32 i = 0; i < allDrives.size(); i++) {
00149                     if (allDrives[i].fsType == "cddafs" && findBaseDiskNumber(allDrives[i].deviceName) == baseDiskNumber) {
00150                         debug(1, "Preferring drive '%s'", allDrives[i].mountPoint.c_str());
00151                         cddaDrives.push_back(allDrives[i]);
00152                         allDrives.remove_at(i);
00153                         break;
00154                     }
00155                 }
00156             }
00157         }
00158     }
00159 
00160     // Add the remaining CDDA drives to the CDDA list
00161     for (uint32 i = 0; i < allDrives.size(); i++)
00162         if (allDrives[i].fsType == "cddafs")
00163             cddaDrives.push_back(allDrives[i]);
00164 
00165     if (drive >= (int)cddaDrives.size())
00166         return false;
00167 
00168     debug(1, "Using '%s' as the CD drive", cddaDrives[drive].mountPoint.c_str());
00169 
00170     return findTrackNames(cddaDrives[drive].mountPoint);
00171 }
00172 
00173 bool MacOSXAudioCDManager::openCD(const Common::String &drive) {
00174     DriveList drives = detectAllDrives();
00175 
00176     for (uint32 i = 0; i < drives.size(); i++) {
00177         if (drives[i].fsType != "cddafs")
00178             continue;
00179 
00180         if (drives[i].mountPoint == drive || drives[i].deviceName == drive) {
00181             debug(1, "Using '%s' as the CD drive", drives[i].mountPoint.c_str());
00182             return findTrackNames(drives[i].mountPoint);
00183         }
00184     }
00185 
00186     return false;
00187 }
00188 
00189 void MacOSXAudioCDManager::close() {
00190     DefaultAudioCDManager::close();
00191     _trackMap.clear();
00192 }
00193 
00194 enum {
00195     // Some crazy high number that we'll never actually hit
00196     kMaxDriveCount = 256
00197 };
00198 
00199 MacOSXAudioCDManager::DriveList MacOSXAudioCDManager::detectAllDrives() {
00200     // Fetch the lists of drives
00201     struct statfs driveStats[kMaxDriveCount];
00202     int foundDrives = getfsstat(driveStats, sizeof(driveStats), MNT_WAIT);
00203     if (foundDrives <= 0)
00204         return DriveList();
00205 
00206     DriveList drives;
00207     for (int i = 0; i < foundDrives; i++)
00208         drives.push_back(Drive(driveStats[i].f_mntonname, driveStats[i].f_mntfromname, driveStats[i].f_fstypename));
00209 
00210     return drives;
00211 }
00212 
00213 bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
00214         Audio::Mixer::SoundType soundType) {
00215     // Prefer emulation
00216     if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate, soundType))
00217         return true;
00218 
00219     // If we're set to only emulate, or have no CD drive, return here
00220     if (onlyEmulate || !_trackMap.contains(track))
00221         return false;
00222 
00223     if (!numLoops && !startFrame)
00224         return false;
00225 
00226     // Now load the AIFF track from the name
00227     Common::String fileName = _trackMap[track];
00228     Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.c_str(), false);
00229 
00230     if (!stream) {
00231         warning("Failed to open track '%s'", fileName.c_str());
00232         return false;
00233     }
00234 
00235     Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
00236     if (!audioStream) {
00237         warning("Track '%s' is not an AIFF track", fileName.c_str());
00238         return false;
00239     }
00240 
00241     Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream);
00242     if (!seekStream) {
00243         warning("Track '%s' is not seekable", fileName.c_str());
00244         return false;
00245     }
00246 
00247     Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
00248     Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : seekStream->getLength();
00249 
00250     // Fake emulation since we're really playing an AIFF file
00251     _emulating = true;
00252 
00253     _mixer->playStream(soundType, &_handle,
00254                        Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
00255     return true;
00256 }
00257 
00258 bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) {
00259     Common::FSNode directory(drivePath);
00260 
00261     if (!directory.exists()) {
00262         warning("Directory '%s' does not exist", drivePath.c_str());
00263         return false;
00264     }
00265 
00266     if (!directory.isDirectory()) {
00267         warning("'%s' is not a directory", drivePath.c_str());
00268         return false;
00269     }
00270 
00271     Common::FSList children;
00272     if (!directory.getChildren(children, Common::FSNode::kListFilesOnly)) {
00273         warning("Failed to find children for '%s'", drivePath.c_str());
00274         return false;
00275     }
00276 
00277     for (uint32 i = 0; i < children.size(); i++) {
00278         if (!children[i].isDirectory()) {
00279             Common::String fileName = children[i].getName();
00280 
00281             if (fileName.hasSuffix(".aiff") || fileName.hasSuffix(".cdda")) {
00282                 uint j = 0;
00283 
00284                 // Search for the track ID in the file name.
00285                 for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++)
00286                     ;
00287 
00288                 const char *trackIDString = fileName.c_str() + j;
00289                 char *endPtr = nullptr;
00290                 long trackID = strtol(trackIDString, &endPtr, 10);
00291 
00292                 if (trackIDString != endPtr && trackID > 0 && trackID < UINT_MAX) {
00293                     _trackMap[trackID - 1] = drivePath + '/' + fileName;
00294                 } else {
00295                     warning("Invalid track file name: '%s'", fileName.c_str());
00296                 }
00297             }
00298         }
00299     }
00300 
00301     return true;
00302 }
00303 
00304 AudioCDManager *createMacOSXAudioCDManager() {
00305     return new MacOSXAudioCDManager();
00306 }
00307 
00308 #endif // MACOSX


Generated on Sat Dec 7 2019 05:00:24 for ResidualVM by doxygen 1.7.1
curved edge   curved edge