/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /************************************* * * USED IN: * Classical Cats * The Daedalus Encounter * Refixion II * *************************************/ /* -! Category compactDisc * -! Title AppleCD SC * -! Protocol Ortho-Play 1.6.1 * --© 1990,1991 MacroMind, Inc. Alan McNeil * ------------------------------------------------------ * --AppleCD SC, version 2.5.6 * --Mar 7 1994 * --including AppleCD SC Plus volume control * ------------------------------------------------------ * X mNew * X mDispose * -- * I mGetMaxNodes * SI mGetNodeTitle NodeNum * II mSelectNode NodeNum * -- * I mService * I mGetValue * I mCancel * S mExplain * I mIdle * -- * X mReadStatus * X mReadPos * XI mSearchTo position * X mPlay * X mStill * X mStop * X mScanForward * X mScanReverse * X mEject * -- * I mGetFirstTrack * I mGetLastTrack * II mGetFirstFrame tracknum * II mGetLastFrame tracknum * I mGetTrack * XII mAudioEnable number number * I mGetFrameResolution * -- * II mSetInPoint frame * II mSetOutPoint frame * II mSetDuration frames * -- * X mPlayCue * X mPlaySegment * -- * S mTitle * SI mTrackName tracknum * IS mSetTitle string * IIS mSetTrackName tracknum string * II mGetTrackType tracknum * III mSetVolume leftVolume rightVolume * II mGetVolume leftFlag * -- */ #include "backends/audiocd/audiocd.h" #include "common/file.h" #include "common/formats/cue.h" #include "director/director.h" #include "director/lingo/lingo.h" #include "director/lingo/lingo-object.h" #include "director/lingo/xlibs/a/applecdxobj.h" namespace Director { const char *const AppleCDXObj::xlibName = "AppleCD"; const XlibFileDesc AppleCDXObj::fileNames[] = { { "AppleCD", nullptr }, { "AppleCD XObj", nullptr }, { nullptr, nullptr }, }; static const MethodProto xlibMethods[] = { { "new", AppleCDXObj::m_new, 0, 0, 300 }, // D3 { "dispose", AppleCDXObj::m_dispose, 0, 0, 300 }, // D3 { "Service", AppleCDXObj::m_service, 0, 0, 300 }, // D4 { "Still", AppleCDXObj::m_still, 0, 0, 300 }, // D3 { "ReadStatus", AppleCDXObj::m_readStatus, 0, 0, 300 }, // D3 { "GetValue", AppleCDXObj::m_getValue, 0, 0, 300 }, // D3 { "Eject", AppleCDXObj::m_eject, 0, 0, 300 }, // D3 { "GetFirstTrack", AppleCDXObj::m_getFirstTrack, 0, 0, 300 }, // D3 { "GetLastTrack", AppleCDXObj::m_getLastTrack, 0, 0, 300 }, // D3 { "GetFirstFrame", AppleCDXObj::m_getFirstFrame, 1, 1, 300 }, // D3 { "GetLastFrame", AppleCDXObj::m_getLastFrame, 1, 1, 300 }, // D3 { "SetInPoint", AppleCDXObj::m_setInPoint, 1, 1, 300 }, // D3 { "SetOutPoint", AppleCDXObj::m_setOutPoint, 1, 1, 300 }, // D3 { "PlayCue", AppleCDXObj::m_playCue, 0, 0, 300 }, // D3 { "PlaySegment", AppleCDXObj::m_playSegment, 0, 0, 300 }, // D3 { "ReadPos", AppleCDXObj::m_readPos, 0, 0, 300 }, // D3 { nullptr, nullptr, 0, 0, 0 } }; void AppleCDXObj::open(ObjectType type, const Common::Path &path) { if (type == kXObj) { AppleCDXObject::initMethods(xlibMethods); AppleCDXObject *xobj = new AppleCDXObject(kXObj); g_lingo->exposeXObject(xlibName, xobj); } } void AppleCDXObj::close(ObjectType type) { if (type == kXObj) { AppleCDXObject::cleanupMethods(); g_lingo->_globalvars[xlibName] = Datum(); } } AppleCDXObject::AppleCDXObject(ObjectType ObjectType) :Object("AppleCD") { _objType = ObjectType; _inpoint = 0; _outpoint = 0; Common::File cuefile; if (cuefile.open("disc.cue")) { Common::String cuestring = cuefile.readString(0, cuefile.size()); _cue = Common::SharedPtr(new Common::CueSheet(cuestring.c_str())); } } void AppleCDXObj::m_new(int nargs) { g_lingo->push(g_lingo->_state->me); } void AppleCDXObj::m_dispose(int nargs) { g_director->_system->getAudioCDManager()->stop(); } void AppleCDXObj::m_still(int nargs) { g_director->_system->getAudioCDManager()->stop(); } // Not implemented yet; needs to be able to return appropriate values // for playing/paused. void AppleCDXObj::m_service(int nargs) { g_lingo->push(Datum(0)); } void AppleCDXObj::m_readStatus(int nargs) { } void AppleCDXObj::m_getValue(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); g_lingo->push(Datum(me->_returnValue)); } void AppleCDXObj::m_setInPoint(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); int inpoint = g_lingo->pop().asInt(); debug(5, "AppleCDXObj::setInPoint: %i", inpoint); me->_inpoint = inpoint; } void AppleCDXObj::m_setOutPoint(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); int outpoint = g_lingo->pop().asInt(); debug(5, "AppleCDXObj::setOutPoint: %i", outpoint); me->_outpoint = outpoint; } void AppleCDXObj::m_playCue(int nargs) { // Essentially a noop for us; this asks the drive to seek to that point, // then poll until it does. We don't have seek times, so we'll // simply noop. } void AppleCDXObj::m_playSegment(int nargs) { // Performs playback at the pre-specified absolute point on the disc, // using the values from setInPoint and setOutPoint AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); g_director->_system->getAudioCDManager()->playAbsolute(me->_inpoint, -1, 0, 0); } void AppleCDXObj::m_readPos(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); AudioCDManager::Status status = g_director->_system->getAudioCDManager()->getStatus(); if (me->_cue) { // ScummVM currently doesn't support specifying the exact frame, so we pretend we're at the first frame of the song Common::CueSheet::CueTrack *track = me->_cue->getTrack(status.track); if (track != nullptr) { me->_returnValue = track->indices[0]; } } } void AppleCDXObj::m_getFirstTrack(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); if (me->_cue) { Common::Array tracks = me->_cue->tracks(); // If we have a set of tracks parsed, return the first track's number int index; if (tracks.size() >= 1) { index = tracks[0].number; } else { index = 1; } debug(5, "AppleCDXObj::m_getFirstTrack: returning %i", index); g_lingo->push(Datum(index)); } else { // If we don't have a TOC, just assume the first track is 1 debug(5, "AppleCDXObj::m_getFirstTrack: returning default"); g_lingo->push(Datum(1)); } } void AppleCDXObj::m_getLastTrack(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); if (me->_cue) { Common::Array tracks = me->_cue->tracks(); // If we have a set of tracks parsed, return the final track's number int index; if (tracks.size() >= 1) { index = tracks.back().number; } else { index = 1; } debug(5, "AppleCDXObj::m_getLastTrack: returning %i", index); } else { // If we don't have a TOC, just assume the last track is 1 debug(5, "AppleCDXObj::m_getLastTrack: returning default"); g_lingo->push(Datum(1)); } } void AppleCDXObj::m_getFirstFrame(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); int trackNum = g_lingo->pop().asInt(); if (me->_cue) { Common::CueSheet::CueTrack *track = me->_cue->getTrack(trackNum); // We use index 1 here because index 0 is typically the // pregap, and we don't want to describe the first sector of the // pregap as being the first sector of the track. Most discs // will have only two indices, and the second index is where the // actual track begins. int index = track->indices.size() > 1 ? track->indices[1] : track->indices[0]; debug(5, "AppleCDXObj::m_getFirstFrame(%i): returning %i", trackNum, index); g_lingo->push(Datum(index)); } else { // If we don't have a TOC, just provide a stub value debug(5, "AppleCDXObj::m_getFirstFrame(%i): returning default", trackNum); g_lingo->push(Datum(0)); } } // Currently calculated based on the start of the next track, since // cuesheets don't contain the duration of a song. void AppleCDXObj::m_getLastFrame(int nargs) { AppleCDXObject *me = static_cast(g_lingo->_state->me.u.obj); int trackNum = g_lingo->pop().asInt(); if (me->_cue) { // We look for the pregap of the next track, if there is one. // TODO opening the actual audio track and getting its length // in sectors would produce a more accurate result for the final track Common::CueSheet::CueTrack *track = me->_cue->getTrack(trackNum + 1); int index; if (track) { // Don't use the pregap if there is no pregap index = track->indices[0] == -1 ? track->indices[1] : track->indices[0]; } else { debug(5, "AppleCDXObj::m_getLastFrame(%i): no track at trackNum %i, setting index to 0", trackNum, trackNum + 1); index = 0; } debug(5, "AppleCDXObj::m_getLastFrame(%i): returning %i", trackNum, index); g_lingo->push(Datum(index)); } else { // If we don't have a TOC, just provide a stub value debug(5, "AppleCDXObj::m_getLastFrame(%i): returning default", trackNum); g_lingo->push(Datum(0)); } } void AppleCDXObj::m_eject(int nargs) { debug(5, "AppleCDXObj::eject: Ejecting CD"); } } // End of namespace Director