diff options
-rw-r--r-- | AUTHORS | 14 | ||||
-rw-r--r-- | audio/decoders/quicktime.cpp | 7 | ||||
-rw-r--r-- | common/quicktime.cpp | 19 | ||||
-rw-r--r-- | common/quicktime.h | 2 | ||||
-rwxr-xr-x | devtools/credits.pl | 14 | ||||
-rwxr-xr-x | devtools/tasmrecover/tasm-recover | 10 | ||||
-rw-r--r-- | engines/dreamweb/dreambase.h | 7 | ||||
-rw-r--r-- | engines/dreamweb/dreamgen.cpp | 456 | ||||
-rw-r--r-- | engines/dreamweb/dreamgen.h | 12 | ||||
-rw-r--r-- | engines/dreamweb/people.cpp | 15 | ||||
-rw-r--r-- | engines/dreamweb/sprite.cpp | 2 | ||||
-rw-r--r-- | engines/dreamweb/stubs.cpp | 244 | ||||
-rw-r--r-- | engines/dreamweb/stubs.h | 10 | ||||
-rw-r--r-- | engines/dreamweb/use.cpp | 27 | ||||
-rw-r--r-- | engines/mohawk/graphics.cpp | 15 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/myst.cpp | 8 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/stoneship.cpp | 4 | ||||
-rw-r--r-- | engines/mohawk/video.cpp | 9 | ||||
-rw-r--r-- | engines/mohawk/video.h | 1 | ||||
-rw-r--r-- | gui/credits.h | 7 | ||||
-rw-r--r-- | video/qt_decoder.cpp | 808 | ||||
-rw-r--r-- | video/qt_decoder.h | 114 |
22 files changed, 980 insertions, 825 deletions
@@ -32,7 +32,7 @@ ScummVM Team Max Horn - (retired) Travis Howell Pawel Kolodziejski - Codecs, iMUSE, Smush, etc. - Gregory Montoir + Gregory Montoir - (retired) Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection, Herc/CGA Ludvig Strigeus - (retired) @@ -40,7 +40,7 @@ ScummVM Team HE: Jonathan Gray - (retired) Travis Howell - Gregory Montoir + Gregory Montoir - (retired) Eugene Sandulenko AGI: @@ -67,7 +67,7 @@ ScummVM Team Cine: Vincent Hamm - (retired) Pawel Kolodziejski - Gregory Montoir + Gregory Montoir - (retired) Kari Salminen Eugene Sandulenko @@ -111,7 +111,7 @@ ScummVM Team Torbjorn Andersson - VQA Player Oystein Eftevaag Florian Kagerer - Gregory Montoir + Gregory Montoir - (retired) Johannes Schickel Lastexpress: @@ -139,7 +139,7 @@ ScummVM Team Queen: David Eriksson - (retired) - Gregory Montoir + Gregory Montoir - (retired) Joost Peters SAGA: @@ -198,14 +198,14 @@ ScummVM Team Sylvain Dupont Touche: - Gregory Montoir + Gregory Montoir - (retired) TsAGE: Arnaud Boutonne Paul Gilbert Tucker: - Gregory Montoir + Gregory Montoir - (retired) Backend Teams ------------- diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 8cf0305e88..e737bf8e10 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -87,6 +87,9 @@ void QuickTimeAudioDecoder::init() { // Initialize the codec (if necessary) entry->initCodec(); + + if (_tracks[_audioTrackIndex]->editCount != 1) + warning("Multiple edit list entries in an audio track. Things may go awry"); } } } @@ -414,7 +417,9 @@ public: } Timestamp getLength() const { - return Timestamp(0, _tracks[_audioTrackIndex]->duration, _tracks[_audioTrackIndex]->timeScale); + // TODO: Switch to the other one when audio edits are supported + //return Timestamp(0, _tracks[_audioTrackIndex]->duration, _timeScale); + return Timestamp(0, _tracks[_audioTrackIndex]->mediaDuration, _tracks[_audioTrackIndex]->timeScale); } }; diff --git a/common/quicktime.cpp b/common/quicktime.cpp index 9ea8c229ea..e16d3f2652 100644 --- a/common/quicktime.cpp +++ b/common/quicktime.cpp @@ -386,8 +386,7 @@ int QuickTimeParser::readTKHD(Atom atom) { /* track->id = */_fd->readUint32BE(); // track id (NOT 0 !) _fd->readUint32BE(); // reserved - //track->startTime = 0; // check - (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase + track->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase _fd->readUint32BE(); // reserved _fd->readUint32BE(); // reserved @@ -410,8 +409,8 @@ int QuickTimeParser::readTKHD(Atom atom) { track->scaleFactorY.debugPrint(1, "readTKHD(): scaleFactorY ="); // these are fixed-point, 16:16 - // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width - // uint32 tkHeight = _fd->readUint32BE() >> 16; // track height + //_fd->readUint32BE() >> 16; // track width + //_fd->readUint32BE() >> 16; // track height return 0; } @@ -428,17 +427,18 @@ int QuickTimeParser::readELST(Atom atom) { debug(2, "Track %d edit list count: %d", _tracks.size() - 1, track->editCount); + uint32 offset = 0; + for (uint32 i = 0; i < track->editCount; i++){ track->editList[i].trackDuration = _fd->readUint32BE(); track->editList[i].mediaTime = _fd->readSint32BE(); track->editList[i].mediaRate = Rational(_fd->readUint32BE(), 0x10000); - debugN(3, "\tDuration = %d, Media Time = %d, ", track->editList[i].trackDuration, track->editList[i].mediaTime); + track->editList[i].timeOffset = offset; + debugN(3, "\tDuration = %d (Offset = %d), Media Time = %d, ", track->editList[i].trackDuration, offset, track->editList[i].mediaTime); track->editList[i].mediaRate.debugPrint(3, "Media Rate ="); + offset += track->editList[i].trackDuration; } - if (track->editCount != 1) - warning("Multiple edit list entries. Things may go awry"); - return 0; } @@ -500,7 +500,7 @@ int QuickTimeParser::readMDHD(Atom atom) { } track->timeScale = _fd->readUint32BE(); - track->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration + track->mediaDuration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration _fd->readUint16BE(); // language _fd->readUint16BE(); // quality @@ -793,6 +793,7 @@ QuickTimeParser::Track::Track() { duration = 0; startTime = 0; objectTypeMP4 = 0; + mediaDuration = 0; } QuickTimeParser::Track::~Track() { diff --git a/common/quicktime.h b/common/quicktime.h index e4c821e209..d7e2691c2b 100644 --- a/common/quicktime.h +++ b/common/quicktime.h @@ -109,6 +109,7 @@ protected: struct EditListEntry { uint32 trackDuration; + uint32 timeOffset; int32 mediaTime; Rational mediaRate; }; @@ -163,6 +164,7 @@ protected: uint32 frameCount; uint32 duration; + uint32 mediaDuration; uint32 startTime; Rational scaleFactorX; Rational scaleFactorY; diff --git a/devtools/credits.pl b/devtools/credits.pl index 2833236e75..86ba7412fa 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -484,7 +484,7 @@ begin_credits("Credits"); add_person("Max Horn", "Fingolfin", "(retired)"); add_person("Travis Howell", "Kirben", ""); add_person("Paweł Kołodziejski", "aquadran", "Codecs, iMUSE, Smush, etc."); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); add_person("Eugene Sandulenko", "sev", "FT INSANE, MM NES, MM C64, game detection, Herc/CGA"); add_person("Ludvig Strigeus", "ludde", "(retired)"); end_section(); @@ -492,7 +492,7 @@ begin_credits("Credits"); begin_section("HE"); add_person("Jonathan Gray", "khalek", "(retired)"); add_person("Travis Howell", "Kirben", ""); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); add_person("Eugene Sandulenko", "sev", ""); end_section(); @@ -523,7 +523,7 @@ begin_credits("Credits"); begin_section("Cine"); add_person("Vincent Hamm", "yaz0r", "(retired)"); add_person("Paweł Kołodziejski", "aquadran", ""); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); add_person("Kari Salminen", "Buddha^", ""); add_person("Eugene Sandulenko", "sev", ""); end_section(); @@ -576,7 +576,7 @@ begin_credits("Credits"); add_person("Torbjörn Andersson", "eriktorbjorn", "VQA Player"); add_person("Oystein Eftevaag", "vinterstum", ""); add_person("Florian Kagerer", "athrxx", ""); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); add_person("Johannes Schickel", "LordHoto", ""); end_section(); @@ -610,7 +610,7 @@ begin_credits("Credits"); begin_section("Queen"); add_person("David Eriksson", "twogood", "(retired)"); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); add_person("Joost Peters", "joostp", ""); end_section(); @@ -679,7 +679,7 @@ begin_credits("Credits"); end_section(); begin_section("Touché"); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); end_section(); begin_section("TsAGE"); @@ -688,7 +688,7 @@ begin_credits("Credits"); end_section(); begin_section("Tucker"); - add_person("Gregory Montoir", "cyx", ""); + add_person("Gregory Montoir", "cyx", "(retired)"); end_section(); end_section(); diff --git a/devtools/tasmrecover/tasm-recover b/devtools/tasmrecover/tasm-recover index 8ddfed4aa7..ef04a2c801 100755 --- a/devtools/tasmrecover/tasm-recover +++ b/devtools/tasmrecover/tasm-recover @@ -222,6 +222,7 @@ generator = cpp(context, "DreamGen", blacklist = [ 'animpointer', 'atmospheres', 'attendant', + 'autoappear', 'autolook', 'autosetwalk', 'backobject', @@ -356,6 +357,8 @@ generator = cpp(context, "DreamGen", blacklist = [ 'endpaltostart', 'entercode', 'entersymbol', + 'entryanims', + 'entrytexts', 'eraseoldobs', 'error', 'errormessage1', @@ -430,6 +433,7 @@ generator = cpp(context, "DreamGen", blacklist = [ 'getroomspaths', 'gettime', 'gettingshot', + 'getundercentre', 'getundermenu', 'getundertimed', 'getunderzoom', @@ -601,12 +605,15 @@ generator = cpp(context, "DreamGen", blacklist = [ 'printslow', 'printsprites', 'printundermon', + 'processtrigger', 'putbackobstuff', + 'putundercentre', 'putundermenu', 'putundertimed', 'putunderzoom', 'quickquit', 'quickquit2', + 'quitkey', 'quitsymbol', 'random', 'randomaccess', @@ -671,6 +678,7 @@ generator = cpp(context, "DreamGen", blacklist = [ 'setupemm', 'setuppit', 'setuptimedtemp', + 'setuptimeduse', 'setwalk', 'showallex', 'showallfree', @@ -754,6 +762,7 @@ generator = cpp(context, "DreamGen", blacklist = [ 'transferinv', 'transfertext', 'trapdoor', + 'triggermessage', 'trysoundalloc', 'turnanypathoff', 'turnanypathon', @@ -805,6 +814,7 @@ generator = cpp(context, "DreamGen", blacklist = [ 'usewall', 'usewinch', 'usewindow', + 'usewire', 'viewfolder', 'vsync', 'volumeadjust', diff --git a/engines/dreamweb/dreambase.h b/engines/dreamweb/dreambase.h index 3d5c9ce88b..c7e9546069 100644 --- a/engines/dreamweb/dreambase.h +++ b/engines/dreamweb/dreambase.h @@ -115,6 +115,7 @@ public: const Frame *getReelFrameAX(uint16 frame); void soundOnReels(uint16 reelPointer); void rollEndCredits(); + void priestText(ReelRoutine &routine); // from stubs.cpp void crosshair(); @@ -154,6 +155,7 @@ public: // from use.cpp void placeFreeObject(uint8 index); void removeFreeObject(uint8 index); + void setupTimedUse(uint16 offset, uint16 countToTimed, uint16 timeCount, byte x, byte y); // from vgafades.cpp uint8 *mainPalette(); @@ -182,6 +184,10 @@ public: inline uint8 *workspace() { return _workspace; } void clearWork(); + uint8 getLocation(uint8 index); + void setLocation(uint8 index); + void getUnderCentre(); + void putUnderCentre(); uint8 *mapStore(); void panelToMap(); void mapToPanel(); @@ -209,6 +215,7 @@ public: void createPanel(); void createPanel2(); void showPanel(); + void entryTexts(); }; diff --git a/engines/dreamweb/dreamgen.cpp b/engines/dreamweb/dreamgen.cpp index 7276445b7f..d98b238797 100644 --- a/engines/dreamweb/dreamgen.cpp +++ b/engines/dreamweb/dreamgen.cpp @@ -2821,28 +2821,6 @@ _tmp2: workToScreenM(); } -void DreamGenContext::getUnderCentre() { - STACK_CHECK; - di = 58; - bx = 72; - ds = data.word(kMapstore); - si = 0; - cl = 254; - ch = 110; - multiGet(); -} - -void DreamGenContext::putUnderCentre() { - STACK_CHECK; - di = 58; - bx = 72; - ds = data.word(kMapstore); - si = 0; - cl = 254; - ch = 110; - multiPut(); -} - void DreamGenContext::locationPic() { STACK_CHECK; getDestInfo(); @@ -3544,68 +3522,6 @@ finishpars: di = offset_operand1; } -void DreamGenContext::processTrigger() { - STACK_CHECK; - _cmp(data.byte(kLasttrigger), '1'); - if (!flags.z()) - goto notfirsttrigger; - al = 8; - setLocation(); - al = 45; - triggerMessage(); - return; -notfirsttrigger: - _cmp(data.byte(kLasttrigger), '2'); - if (!flags.z()) - goto notsecondtrigger; - al = 9; - setLocation(); - al = 55; - triggerMessage(); - return; -notsecondtrigger: - _cmp(data.byte(kLasttrigger), '3'); - if (!flags.z()) - return /* (notthirdtrigger) */; - al = 2; - setLocation(); - al = 59; - triggerMessage(); -} - -void DreamGenContext::triggerMessage() { - STACK_CHECK; - push(ax); - di = 174; - bx = 153; - cl = 200; - ch = 63; - ds = data.word(kMapstore); - si = 0; - multiGet(); - ax = pop(); - findPuzText(); - di = 174; - bx = 156; - dl = 141; - ah = 16; - printDirect(); - cx = 140; - hangOn(); - workToScreen(); - cx = 340; - hangOn(); - di = 174; - bx = 153; - cl = 200; - ch = 63; - ds = data.word(kMapstore); - si = 0; - multiPut(); - workToScreen(); - data.byte(kLasttrigger) = 0; -} - void DreamGenContext::runTap() { STACK_CHECK; _cmp(data.byte(kWithobject), 255); @@ -4135,55 +4051,6 @@ axeoncontrols: putBackObStuff(); } -void DreamGenContext::useWire() { - STACK_CHECK; - _cmp(data.byte(kWithobject), 255); - if (!flags.z()) - goto gotwirewith; - withWhat(); - return; -gotwirewith: - al = data.byte(kWithobject); - ah = data.byte(kWithtype); - cl = 'K'; - ch = 'N'; - dl = 'F'; - dh = 'E'; - compare(); - if (flags.z()) - goto wireknife; - al = data.byte(kWithobject); - ah = data.byte(kWithtype); - cl = 'A'; - ch = 'X'; - dl = 'E'; - dh = 'D'; - compare(); - if (flags.z()) - goto wireaxe; - cx = 300; - al = 14; - showPuzText(); - putBackObStuff(); - return; -wireaxe: - cx = 300; - al = 16; - showPuzText(); - putBackObStuff(); - return; -wireknife: - al = 51; - removeSetObject(); - al = 52; - placeSetObject(); - cx = 300; - al = 11; - showPuzText(); - _inc(data.byte(kProgresspoints)); - data.byte(kGetback) = 1; -} - void DreamGenContext::useHandle() { STACK_CHECK; al = 'C'; @@ -4580,98 +4447,6 @@ void DreamGenContext::removeFreeObject() { es = pop(); } -void DreamGenContext::autoAppear() { - STACK_CHECK; - _cmp(data.byte(kLocation), 32); - if (!flags.z()) - goto notinalley; - al = 5; - resetLocation(); - al = 10; - setLocation(); - data.byte(kDestpos) = 10; - return; -notinalley: - _cmp(data.byte(kReallocation), 24); - if (!flags.z()) - goto notinedens; - _cmp(data.byte(kGeneraldead), 1); - if (!flags.z()) - goto edenspart2; - _inc(data.byte(kGeneraldead)); - al = 44; - placeSetObject(); - al = 18; - placeSetObject(); - al = 93; - placeSetObject(); - al = 92; - removeSetObject(); - al = 55; - removeSetObject(); - al = 75; - removeSetObject(); - al = 84; - removeSetObject(); - al = 85; - removeSetObject(); - return; -edenspart2: - _cmp(data.byte(kSartaindead), 1); - if (!flags.z()) - return /* (notedens2) */; - al = 44; - removeSetObject(); - al = 93; - removeSetObject(); - al = 55; - placeSetObject(); - _inc(data.byte(kSartaindead)); - return; -notinedens: - _cmp(data.byte(kReallocation), 25); - if (!flags.z()) - goto notonsartroof; - data.byte(kNewsitem) = 3; - al = 6; - resetLocation(); - al = 11; - setLocation(); - data.byte(kDestpos) = 11; - return; -notonsartroof: - _cmp(data.byte(kReallocation), 2); - if (!flags.z()) - return /* (notinlouiss) */; - _cmp(data.byte(kRockstardead), 0); - if (flags.z()) - return /* (notinlouiss) */; - al = 23; - placeSetObject(); -} - -void DreamGenContext::setupTimedUse() { - STACK_CHECK; - _cmp(data.word(kTimecount), 0); - if (!flags.z()) - return /* (cantsetup) */; - data.byte(kTimedy) = bh; - data.byte(kTimedx) = bl; - data.word(kCounttotimed) = cx; - _add(dx, cx); - data.word(kTimecount) = dx; - bl = al; - bh = 0; - _add(bx, bx); - es = data.word(kPuzzletext); - cx = (66*2); - ax = es.word(bx); - _add(ax, cx); - bx = ax; - data.word(kTimedseg) = es; - data.word(kTimedoffset) = bx; -} - void DreamGenContext::useGun() { STACK_CHECK; _cmp(data.byte(kObjecttype), 4); @@ -4849,27 +4624,6 @@ nottvsoldier: putBackObStuff(); } -void DreamGenContext::quitKey() { - STACK_CHECK; - _cmp(data.byte(kCommandtype), 222); - if (flags.z()) - goto alreadyqk; - data.byte(kCommandtype) = 222; - al = 4; - commandOnly(); -alreadyqk: - ax = data.word(kMousebutton); - _cmp(ax, data.word(kOldbutton)); - if (flags.z()) - return /* (notqk) */; - _and(ax, 1); - if (!flags.z()) - goto doqk; - return; -doqk: - data.byte(kGetback) = 1; -} - void DreamGenContext::updateSymbolTop() { STACK_CHECK; _cmp(data.byte(kSymboltopdir), 0); @@ -5191,216 +4945,6 @@ nomatchslot: goto slotloop; } -void DreamGenContext::entryTexts() { - STACK_CHECK; - _cmp(data.byte(kLocation), 21); - if (!flags.z()) - goto notloc15; - al = 28; - cx = 60; - dx = 11; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notloc15: - _cmp(data.byte(kLocation), 30); - if (!flags.z()) - goto notloc43; - al = 27; - cx = 60; - dx = 11; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notloc43: - _cmp(data.byte(kLocation), 23); - if (!flags.z()) - goto notloc23; - al = 29; - cx = 60; - dx = 11; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notloc23: - _cmp(data.byte(kLocation), 31); - if (!flags.z()) - goto notloc44; - al = 30; - cx = 60; - dx = 11; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notloc44: - _cmp(data.byte(kLocation), 20); - if (!flags.z()) - goto notsarters2; - al = 31; - cx = 60; - dx = 11; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notsarters2: - _cmp(data.byte(kLocation), 24); - if (!flags.z()) - goto notedenlob; - al = 32; - cx = 60; - dx = 3; - bl = 68; - bh = 64; - setupTimedUse(); - return; -notedenlob: - _cmp(data.byte(kLocation), 34); - if (!flags.z()) - return /* (noteden2) */; - al = 33; - cx = 60; - dx = 3; - bl = 68; - bh = 64; - setupTimedUse(); -} - -void DreamGenContext::entryAnims() { - STACK_CHECK; - data.word(kReeltowatch) = -1; - data.byte(kWatchmode) = -1; - _cmp(data.byte(kLocation), 33); - if (!flags.z()) - goto notinthebeach; - switchRyanOff(); - data.word(kWatchingtime) = 76*2; - data.word(kReeltowatch) = 0; - data.word(kEndwatchreel) = 76; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - return; -notinthebeach: - _cmp(data.byte(kLocation), 44); - if (!flags.z()) - goto notsparkys; - al = 8; - resetLocation(); - data.word(kWatchingtime) = 50*2; - data.word(kReeltowatch) = 247; - data.word(kEndwatchreel) = 297; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - switchRyanOff(); - return; -notsparkys: - _cmp(data.byte(kLocation), 22); - if (!flags.z()) - goto notinthelift; - data.word(kWatchingtime) = 31*2; - data.word(kReeltowatch) = 0; - data.word(kEndwatchreel) = 30; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - switchRyanOff(); - return; -notinthelift: - _cmp(data.byte(kLocation), 26); - if (!flags.z()) - goto notunderchurch; - data.byte(kSymboltopnum) = 2; - data.byte(kSymbolbotnum) = 1; - return; -notunderchurch: - _cmp(data.byte(kLocation), 45); - if (!flags.z()) - goto notenterdream; - data.byte(kKeeperflag) = 0; - data.word(kWatchingtime) = 296; - data.word(kReeltowatch) = 45; - data.word(kEndwatchreel) = 198; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - switchRyanOff(); - return; -notenterdream: - _cmp(data.byte(kReallocation), 46); - if (!flags.z()) - goto notcrystal; - _cmp(data.byte(kSartaindead), 1); - if (!flags.z()) - goto notcrystal; - al = 0; - removeFreeObject(); - return; -notcrystal: - _cmp(data.byte(kLocation), 9); - if (!flags.z()) - goto nottopchurch; - al = 2; - checkIfPathIsOn(); - if (flags.z()) - goto nottopchurch; - _cmp(data.byte(kAidedead), 0); - if (flags.z()) - goto nottopchurch; - al = 3; - checkIfPathIsOn(); - if (!flags.z()) - goto makedoorsopen; - al = 2; - turnPathOn(); -makedoorsopen: - al = 4; - removeSetObject(); - al = 5; - placeSetObject(); - return; -nottopchurch: - _cmp(data.byte(kLocation), 47); - if (!flags.z()) - goto notdreamcentre; - al = 4; - placeSetObject(); - al = 5; - placeSetObject(); - return; -notdreamcentre: - _cmp(data.byte(kLocation), 38); - if (!flags.z()) - goto notcarpark; - data.word(kWatchingtime) = 57*2; - data.word(kReeltowatch) = 4; - data.word(kEndwatchreel) = 57; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - switchRyanOff(); - return; -notcarpark: - _cmp(data.byte(kLocation), 32); - if (!flags.z()) - goto notalley; - data.word(kWatchingtime) = 66*2; - data.word(kReeltowatch) = 0; - data.word(kEndwatchreel) = 66; - data.byte(kWatchspeed) = 1; - data.byte(kSpeedcount) = 1; - switchRyanOff(); - return; -notalley: - _cmp(data.byte(kLocation), 24); - if (!flags.z()) - return /* (notedensagain) */; - al = 2; - ah = data.byte(kRoomnum); - _dec(ah); - turnAnyPathOn(); -} - void DreamGenContext::clearBuffers() { STACK_CHECK; es = data.word(kBuffers); diff --git a/engines/dreamweb/dreamgen.h b/engines/dreamweb/dreamgen.h index 3da8d8ecb8..746f306b3d 100644 --- a/engines/dreamweb/dreamgen.h +++ b/engines/dreamweb/dreamgen.h @@ -486,15 +486,11 @@ public: void __start(); #include "stubs.h" // Allow hand-reversed functions to have a signature different than void f() - void useWire(); void identifyOb(); void runEndSeq(); void useOpenBox(); void clearBuffers(); void getObTextStart(); - void dumpDiaryKeys(); - void entryTexts(); - void putUnderCentre(); void checkObjectSize(); void findText1(); void isRyanHolding(); @@ -519,12 +515,12 @@ public: void usePipe(); void reminders(); void runTap(); + void dumpDiaryKeys(); void checkForExit(); void lookInInterface(); void inToInv(); void adjustLeft(); void deleteExText(); - void entryAnims(); void getFreeAd(); void removeObFromInv(); void heavy(); @@ -547,7 +543,6 @@ public: void purgeALocation(); void notHeldError(); void getSetAd(); - void getUnderCentre(); void showKeys(); void printmessage2(); void findOpenPos(); @@ -575,13 +570,11 @@ public: void useOpened(); void signOn(); void locationPic(); - void triggerMessage(); void swapWithOpen(); void dreamweb(); void checkInside(); void findPathOfPoint(); void getDestInfo(); - void setupTimedUse(); void makeCaps(); void read(); void additionalText(); @@ -589,7 +582,6 @@ public: void searchForString(); void selectOpenOb(); void useGun(); - void autoAppear(); void useHandle(); void incRyanPage(); void findExObject(); @@ -608,8 +600,6 @@ public: void getPersonText(); void parser(); void emergencyPurge(); - void quitKey(); - void processTrigger(); void transferConToEx(); void adjustDown(); void withWhat(); diff --git a/engines/dreamweb/people.cpp b/engines/dreamweb/people.cpp index 889a430a22..84f6543a19 100644 --- a/engines/dreamweb/people.cpp +++ b/engines/dreamweb/people.cpp @@ -321,7 +321,7 @@ void DreamGenContext::madmansTelly(ReelRoutine &routine) { void DreamGenContext::smokeBloke(ReelRoutine &routine) { if (data.byte(kRockstardead) == 0) { if (routine.b7 & 128) - setLocation(5); + DreamBase::setLocation(5); } if (checkSpeed(routine)) { if (routine.reelPointer() == 100) { @@ -629,25 +629,16 @@ void DreamGenContext::priest(ReelRoutine &routine) { if (checkSpeed(routine)) { routine.incReelPointer(); - push(es); - push(bx); priestText(routine); - bx = pop(); - es = pop(); } } -void DreamGenContext::priestText(ReelRoutine &routine) { +void DreamBase::priestText(ReelRoutine &routine) { uint16 reel = routine.reelPointer(); if (reel < 2 || reel >= 7 || (reel & 1)) return; // nopriesttext - al = ((reel & 0xFF) >> 1) + 50; - bl = 72; - bh = 80; - cx = 54; - dx = 1; - setupTimedUse(); + setupTimedUse((reel >> 1) + 50, 54, 1, 72, 80); } void DreamGenContext::monkAndRyan(ReelRoutine &routine) { diff --git a/engines/dreamweb/sprite.cpp b/engines/dreamweb/sprite.cpp index e93a77f3ff..aca935cde9 100644 --- a/engines/dreamweb/sprite.cpp +++ b/engines/dreamweb/sprite.cpp @@ -763,7 +763,7 @@ void DreamBase::rollEndCredits() { playChannel0(16, 255); data.byte(kVolume) = 7; data.byte(kVolumeto) = 0; - data.byte(kVolumedirection) = 0xFF; + data.byte(kVolumedirection) = (byte)-1; multiGet(mapStore(), 75, 20, 160, 160); diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index 71a6834b73..e52ccf03f3 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -576,7 +576,7 @@ void DreamGenContext::dreamweb() { data.byte(kLastflag) = 32; startup1(); data.byte(kVolumeto) = 0; - data.byte(kVolumedirection) = (uint8)-1; + data.byte(kVolumedirection) = (byte)-1; data.byte(kCommandtype) = 255; } @@ -862,6 +862,41 @@ void DreamGenContext::putUnderTimed() { multiPut(ds.ptr(si, 0), data.byte(kTimedx), y, 240, kUndertimedysize); } +void DreamBase::getUnderCentre() { + multiGet(mapStore(), 58, 72, 254, 110); +} + +void DreamBase::putUnderCentre() { + multiPut(mapStore(), 58, 72, 254, 110); +} + +void DreamGenContext::triggerMessage(uint16 index) { + multiGet(mapStore(), 174, 153, 200, 63); + uint16 offset = kTextstart + getSegment(data.word(kPuzzletext)).word(index * 2); + const uint8 *string = getSegment(data.word(kPuzzletext)).ptr(offset, 0); + uint16 y = 156; + printDirect(&string, 174, &y, 141, true); + hangOn(140); + workToScreenCPP(); + hangOn(340); + multiPut(mapStore(), 174, 153, 200, 63); + workToScreenCPP(); + data.byte(kLasttrigger) = 0; +} + +void DreamGenContext::processTrigger() { + if (data.byte(kLasttrigger) == '1') { + DreamBase::setLocation(8); + triggerMessage(45); + } else if (data.byte(kLasttrigger) == '2') { + DreamBase::setLocation(9); + triggerMessage(55); + } else if (data.byte(kLasttrigger) == '3') { + DreamBase::setLocation(2); + triggerMessage(59); + } +} + void DreamGenContext::useTimedText() { if (data.word(kTimecount) == 0) return; @@ -2466,20 +2501,20 @@ void DreamGenContext::examIcon() { showFrame(engine->icons2(), 254, 5, 3, 0); } -uint8 DreamGenContext::getLocation(uint8 index) { +uint8 DreamBase::getLocation(uint8 index) { return data.byte(kRoomscango + index); } void DreamGenContext::getLocation() { - al = getLocation(al); + al = DreamBase::getLocation(al); } -void DreamGenContext::setLocation(uint8 index) { +void DreamBase::setLocation(uint8 index) { data.byte(kRoomscango + index) = 1; } void DreamGenContext::setLocation() { - setLocation(al); + DreamBase::setLocation(al); } const uint8 *DreamBase::getTextInFile1(uint16 index) { @@ -2542,8 +2577,8 @@ void DreamGenContext::lastFolder() { void DreamGenContext::folderHints() { if (data.byte(kFolderpage) == 5) { - if ((data.byte(kAidedead) != 1) && (getLocation(13) != 1)) { - setLocation(13); + if ((data.byte(kAidedead) != 1) && (DreamBase::getLocation(13) != 1)) { + DreamBase::setLocation(13); showFolder(); const uint8 *string = getTextInFile1(30); printDirect(string, 0, 86, 141, true); @@ -2551,8 +2586,8 @@ void DreamGenContext::folderHints() { hangOnP(200); } } else if (data.byte(kFolderpage) == 9) { - if (getLocation(7) != 1) { - setLocation(7); + if (DreamBase::getLocation(7) != 1) { + DreamBase::setLocation(7); showFolder(); const uint8 *string = getTextInFile1(31); printDirect(string, 0, 86, 141, true); @@ -3031,7 +3066,7 @@ void DreamGenContext::intro() { clearPalette(); loadIntroRoom(); data.byte(kVolume) = 7; - data.byte(kVolumedirection) = (uint8)-1; + data.byte(kVolumedirection) = (byte)-1; data.byte(kVolumeto) = 4; playChannel0(12, 255); fadeScreenUps(); @@ -3247,7 +3282,7 @@ void DreamGenContext::gettingShot() { loadIntroRoom(); fadeScreenUps(); data.byte(kVolumeto) = 0; - data.byte(kVolumedirection) = 0xFF; + data.byte(kVolumedirection) = (byte)-1; runEndSeq(); clearBeforeLoad(); } @@ -3314,8 +3349,8 @@ void DreamGenContext::obsThatDoThings() { if (!compare(data.byte(kCommand), data.byte(kObjecttype), id)) return; // notlouiscard - if (getLocation(4) != 1) { - setLocation(4); + if (DreamBase::getLocation(4) != 1) { + DreamBase::setLocation(4); lookAtCard(); } } @@ -3896,7 +3931,7 @@ void DreamGenContext::talk() { workToScreenM(); if (data.byte(kSpeechloaded) == 1) { cancelCh1(); - data.byte(kVolumedirection) = 0xFF; + data.byte(kVolumedirection) = (byte)-1; data.byte(kVolumeto) = 0; } } @@ -4219,7 +4254,7 @@ void DreamGenContext::monkSpeaking() { showFrame(tempGraphics(), 160, 72, 0, 128); // show monk workToScreen(); data.byte(kVolume) = 7; - data.byte(kVolumedirection) = 0xFF; + data.byte(kVolumedirection) = (byte)-1; data.byte(kVolumeto) = 5; playChannel0(12, 255); fadeScreenUps(); @@ -4262,4 +4297,183 @@ void DreamGenContext::useButtonA() { } } +void DreamGenContext::autoAppear() { + if (data.byte(kLocation) == 32) { + // In alley + al = 5; + resetLocation(); + DreamBase::setLocation(10); + data.byte(kDestpos) = 10; + return; + } + + if (data.byte(kReallocation) == 24) { + // In Eden's apartment + if (data.byte(kGeneraldead) == 1) { + data.byte(kGeneraldead)++; + placeSetObject(44); + placeSetObject(18); + placeSetObject(93); + removeSetObject(92); + removeSetObject(55); + removeSetObject(75); + removeSetObject(84); + removeSetObject(85); + } else if (data.byte(kSartaindead) == 1) { + // Eden's part 2 + removeSetObject(44); + removeSetObject(93); + placeSetObject(55); + data.byte(kSartaindead)++; + } + } else { + // Not in Eden's + if (data.byte(kReallocation) == 25) { + // Sart roof + data.byte(kNewsitem) = 3; + al = 6; + resetLocation(); + DreamBase::setLocation(11); + data.byte(kDestpos) = 11; + } else { + if (data.byte(kReallocation) == 2 && data.byte(kRockstardead) != 0) + placeSetObject(23); + } + } +} + +void DreamGenContext::quitKey() { + if (data.byte(kCommandtype) != 222) { + data.byte(kCommandtype) = 222; + commandOnly(4); + } + + if (data.word(kMousebutton) != data.word(kOldbutton) && (data.word(kMousebutton) & 1)) + data.byte(kGetback) = 1; +} + +void DreamGenContext::setupTimedUse() { + DreamBase::setupTimedUse(al, cx, dx, bl, bh); +} + +void DreamBase::setupTimedUse(uint16 textIndex, uint16 countToTimed, uint16 timeCount, byte x, byte y) { + if (data.word(kTimecount) != 0) + return; // can't setup + + data.byte(kTimedy) = y; + data.byte(kTimedx) = x; + data.word(kCounttotimed) = countToTimed; + data.word(kTimecount) = timeCount + countToTimed; + data.word(kTimedseg) = data.word(kPuzzletext); + data.word(kTimedoffset) = kTextstart + getSegment(data.word(kPuzzletext)).word(textIndex * 2); + const uint8 *string = getSegment(data.word(kPuzzletext)).ptr(data.word(kTimedoffset), 0); + debug(1, "setupTimedUse: %d => '%s'", textIndex, string); +} + +void DreamBase::entryTexts() { + switch (data.byte(kLocation)) { + case 21: + setupTimedUse(28, 60, 11, 68, 64); + break; + case 30: + setupTimedUse(27, 60, 11, 68, 64); + break; + case 23: + setupTimedUse(29, 60, 11, 68, 64); + break; + case 31: + setupTimedUse(30, 60, 11, 68, 64); + break; + case 20: // Sarter's 2 + setupTimedUse(31, 60, 11, 68, 64); + break; + case 24: // Eden's lobby + setupTimedUse(32, 60, 3, 68, 64); + break; + case 34: // Eden 2 + setupTimedUse(33, 60, 3, 68, 64); + break; + default: + break; + } +} + +void DreamGenContext::entryAnims() { + data.word(kReeltowatch) = 0xFFFF; + data.byte(kWatchmode) = (byte)-1; + + switch (data.byte(kLocation)) { + case 33: // beach + switchRyanOff(); + data.word(kWatchingtime) = 76 * 2; + data.word(kReeltowatch) = 0; + data.word(kEndwatchreel) = 76; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + break; + case 44: // Sparky's + al = 8; + resetLocation(); + data.word(kWatchingtime) = 50*2; + data.word(kReeltowatch) = 247; + data.word(kEndwatchreel) = 297; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + switchRyanOff(); + break; + case 22: // lift + data.word(kWatchingtime) = 31 * 2; + data.word(kReeltowatch) = 0; + data.word(kEndwatchreel) = 30; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + switchRyanOff(); + break; + case 26: // under church + data.byte(kSymboltopnum) = 2; + data.byte(kSymbolbotnum) = 1; + break; + case 45: // entered Dreamweb + data.byte(kKeeperflag) = 0; + data.word(kWatchingtime) = 296; + data.word(kReeltowatch) = 45; + data.word(kEndwatchreel) = 198; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + switchRyanOff(); + break; + default: + if (data.byte(kReallocation) == 46 && data.byte(kSartaindead) == 1) { // Crystal + removeFreeObject(0); + } else if (data.byte(kLocation) == 9 && !checkIfPathIsOn(2) && data.byte(kAidedead) != 0) { + // Top of church + if (checkIfPathIsOn(3)) + turnPathOn(2); + + // Make doors open + removeSetObject(4); + placeSetObject(5); + } else if (data.byte(kLocation) == 47) { // Dream centre + placeSetObject(4); + placeSetObject(5); + } else if (data.byte(kLocation) == 38) { // Car park + data.word(kWatchingtime) = 57 * 2; + data.word(kReeltowatch) = 4; + data.word(kEndwatchreel) = 57; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + switchRyanOff(); + } else if (data.byte(kLocation) == 32) { // Alley + data.word(kWatchingtime) = 66 * 2; + data.word(kReeltowatch) = 0; + data.word(kEndwatchreel) = 66; + data.byte(kWatchspeed) = 1; + data.byte(kSpeedcount) = 1; + switchRyanOff(); + } else if (data.byte(kLocation) == 24) { // Eden's again + turnAnyPathOn(2, data.byte(kRoomnum) - 1); + } + } +} + } // End of namespace DreamGen diff --git a/engines/dreamweb/stubs.h b/engines/dreamweb/stubs.h index 4efcc3c07e..7142bec373 100644 --- a/engines/dreamweb/stubs.h +++ b/engines/dreamweb/stubs.h @@ -348,6 +348,7 @@ void useCardReader3(); void usePoolReader(); void useCooker(); + void useWire(); bool defaultUseHandler(const char *id); void openTVDoor(); void wearWatch(); @@ -361,9 +362,7 @@ void lastFolder(); void folderHints(); void folderExit(); - uint8 getLocation(uint8 index); void getLocation(); - void setLocation(uint8 index); void setLocation(); void loadTempText(); void loadTempText(const char *fileName); @@ -407,7 +406,6 @@ void louisChair(ReelRoutine &routine); void bossMan(ReelRoutine &routine); void priest(ReelRoutine &routine); - void priestText(ReelRoutine &routine); void monkAndRyan(ReelRoutine &routine); void copper(ReelRoutine &routine); void introMonks1(ReelRoutine &routine); @@ -565,5 +563,11 @@ void monkSpeaking(); void rollEndCredits2(); void useButtonA(); + void autoAppear(); + void quitKey(); + void setupTimedUse(); + void entryAnims(); + void triggerMessage(uint16 index); + void processTrigger(); #endif diff --git a/engines/dreamweb/use.cpp b/engines/dreamweb/use.cpp index 76ecdc732d..0990e5ba5e 100644 --- a/engines/dreamweb/use.cpp +++ b/engines/dreamweb/use.cpp @@ -1110,6 +1110,33 @@ void DreamGenContext::useLighter() { } } +void DreamGenContext::useWire() { + if (data.byte(kWithobject) == 255) { + withWhat(); + return; + } + + char knife[4] = { 'K', 'N', 'F', 'E' }; // TODO: convert to string with trailing zero + if (compare(data.byte(kWithobject), data.byte(kWithtype), knife)) { + removeSetObject(51); + placeSetObject(52); + showPuzText(11, 300); + data.byte(kProgresspoints)++; + data.byte(kGetback) = 1; + return; + } + + char axe[4] = { 'A', 'X', 'E', 'D' }; // TODO: convert to string with trailing zero + if (compare(data.byte(kWithobject), data.byte(kWithtype), axe)) { + showPuzText(16, 300); + putBackObStuff(); + return; + } + + showPuzText(14, 300); + putBackObStuff(); +} + void DreamGenContext::openTomb() { data.byte(kProgresspoints)++; showFirstUse(); diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index c4326d175f..dcb0c7940d 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -308,16 +308,19 @@ MystGraphics::~MystGraphics() { delete _backBuffer; } -static const char* picFileNames[] = { +static const char *s_picFileNames[] = { "CHpics", "", + "", "DUpics", "INpics", + "", "MEpics", "MYpics", "SEpics", - "STpics", - "" + "", + "", + "STpics" }; void MystGraphics::loadExternalPictureFile(uint16 stack) { @@ -328,11 +331,11 @@ void MystGraphics::loadExternalPictureFile(uint16 stack) { _pictureFile.picFile.close(); delete[] _pictureFile.entries; - if (!scumm_stricmp(picFileNames[stack], "")) + if (!scumm_stricmp(s_picFileNames[stack], "")) return; - if (!_pictureFile.picFile.open(picFileNames[stack])) - error ("Could not open external picture file \'%s\'", picFileNames[stack]); + if (!_pictureFile.picFile.open(s_picFileNames[stack])) + error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index b6c0a3212f..b3222e0322 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -2978,15 +2978,17 @@ void Myst::clockReset() { } void Myst::clockResetWeight() { - // Set video bounds, weight going up + _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); + if (!(_vm->getFeatures() & GF_ME)) { - _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); + // Set video bounds, weight going up _vm->_video->setVideoBounds(_clockWeightVideo, Audio::Timestamp(0, 2214 * 2 - _clockWeightPosition, 600), Audio::Timestamp(0, 2214 * 2, 600)); } else { - //FIXME: Needs QT backwards playing + //FIXME: Needs QT backwards playing, for now just display the weight up warning("Weight going back up not implemented"); + _vm->_video->drawVideoFrame(_clockWeightVideo, Audio::Timestamp(0, 0, 600)); } // Reset position diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp index 4c715b56e4..6d54d0c586 100644 --- a/engines/mohawk/myst_stacks/stoneship.cpp +++ b/engines/mohawk/myst_stacks/stoneship.cpp @@ -623,7 +623,7 @@ void Stoneship::o_hologramSelectionMove(uint16 op, uint16 var, uint16 argc, uint uint16 selectionPos = position * 1500 / 243; VideoHandle handleMovie = _hologramSelection->playMovie(); - _vm->_video->setVideoBounds(handleMovie, Audio::Timestamp(0, selectionPos, 600), Audio::Timestamp(0, selectionPos, 600)); + _vm->_video->drawVideoFrame(handleMovie, Audio::Timestamp(0, selectionPos, 600)); _hologramDisplayPos = position * 1450 / 243 + 350; @@ -631,7 +631,7 @@ void Stoneship::o_hologramSelectionMove(uint16 op, uint16 var, uint16 argc, uint if (_hologramTurnedOn) { _hologramDisplay->setBlocking(false); VideoHandle displayMovie = _hologramDisplay->playMovie(); - _vm->_video->setVideoBounds(displayMovie, Audio::Timestamp(0, _hologramDisplayPos, 600), Audio::Timestamp(0, _hologramDisplayPos, 600)); + _vm->_video->drawVideoFrame(displayMovie, Audio::Timestamp(0, _hologramDisplayPos, 600)); } } } diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index cd8fc8ef80..7958906897 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -529,6 +529,15 @@ void VideoManager::setVideoBounds(VideoHandle handle, Audio::Timestamp start, Au _videoStreams[handle]->seekToTime(start); } +void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { + assert(handle != NULL_VID_HANDLE); + _videoStreams[handle].end = Audio::Timestamp(0xffffffff, 1); + _videoStreams[handle]->seekToTime(time); + updateMovies(); + delete _videoStreams[handle].video; + _videoStreams[handle].clear(); +} + void VideoManager::seekToTime(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); _videoStreams[handle]->seekToTime(time); diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index efa81edfbd..34c287497f 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -104,6 +104,7 @@ public: uint32 getDuration(VideoHandle videoHandle); bool endOfVideo(VideoHandle handle); void setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end); + void drawVideoFrame(VideoHandle handle, Audio::Timestamp time); void seekToTime(VideoHandle handle, Audio::Timestamp time); void setVideoLooping(VideoHandle handle, bool loop); void waitUntilMovieEnds(VideoHandle videoHandle); diff --git a/gui/credits.h b/gui/credits.h index 08be2e0534..dd839c2c3b 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -39,6 +39,7 @@ static const char *credits[] = { "C0""Pawel Kolodziejski", "C2""Codecs, iMUSE, Smush, etc.", "C0""Gregory Montoir", +"C2""(retired)", "C0""Eugene Sandulenko", "C2""FT INSANE, MM NES, MM C64, game detection, Herc/CGA", "C0""Ludvig Strigeus", @@ -49,6 +50,7 @@ static const char *credits[] = { "C2""(retired)", "C0""Travis Howell", "C0""Gregory Montoir", +"C2""(retired)", "C0""Eugene Sandulenko", "", "C1""AGI", @@ -81,6 +83,7 @@ static const char *credits[] = { "C2""(retired)", "C0""Pawel Kolodziejski", "C0""Gregory Montoir", +"C2""(retired)", "C0""Kari Salminen", "C0""Eugene Sandulenko", "", @@ -128,6 +131,7 @@ static const char *credits[] = { "C0""Oystein Eftevaag", "C0""Florian Kagerer", "C0""Gregory Montoir", +"C2""(retired)", "C0""Johannes Schickel", "", "C1""Lastexpress", @@ -157,6 +161,7 @@ static const char *credits[] = { "C0""David Eriksson", "C2""(retired)", "C0""Gregory Montoir", +"C2""(retired)", "C0""Joost Peters", "", "C1""SAGA", @@ -230,6 +235,7 @@ static const char *credits[] = { "", "C1""Touch\351", "C0""Gregory Montoir", +"C2""(retired)", "", "C1""TsAGE", "C0""Arnaud Boutonn\351", @@ -237,6 +243,7 @@ static const char *credits[] = { "", "C1""Tucker", "C0""Gregory Montoir", +"C2""(retired)", "", "", "C1""Backend Teams", diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp index 74bf533e62..4a57ac405d 100644 --- a/video/qt_decoder.cpp +++ b/video/qt_decoder.cpp @@ -56,155 +56,42 @@ namespace Video { //////////////////////////////////////////// QuickTimeDecoder::QuickTimeDecoder() { - _curFrame = -1; - _startTime = _nextFrameStartTime = 0; + _setStartTime = false; _audHandle = Audio::SoundHandle(); _scaledSurface = 0; _dirtyPalette = false; _palette = 0; + _width = _height = 0; + _needUpdate = false; } QuickTimeDecoder::~QuickTimeDecoder() { close(); } -uint16 QuickTimeDecoder::getWidth() const { - if (_videoTrackIndex < 0) - return 0; - - return (Common::Rational(_tracks[_videoTrackIndex]->width) / getScaleFactorX()).toInt(); -} - -uint16 QuickTimeDecoder::getHeight() const { - if (_videoTrackIndex < 0) - return 0; - - return (Common::Rational(_tracks[_videoTrackIndex]->height) / getScaleFactorY()).toInt(); -} - -uint32 QuickTimeDecoder::getFrameCount() const { - if (_videoTrackIndex < 0) - return 0; - - return _tracks[_videoTrackIndex]->frameCount; -} - -Common::Rational QuickTimeDecoder::getScaleFactorX() const { - if (_videoTrackIndex < 0) - return 1; - - return (_scaleFactorX * _tracks[_videoTrackIndex]->scaleFactorX); -} - -Common::Rational QuickTimeDecoder::getScaleFactorY() const { - if (_videoTrackIndex < 0) - return 1; +int32 QuickTimeDecoder::getCurFrame() const { + // TODO: This is rather simplistic and doesn't take edits that + // repeat sections of the media into account. Doing that + // over-complicates things and shouldn't be necessary, but + // it would be nice to have in the future. - return (_scaleFactorY * _tracks[_videoTrackIndex]->scaleFactorY); -} + int32 frame = -1; -uint32 QuickTimeDecoder::getFrameDuration() { - if (_videoTrackIndex < 0) - return 0; + for (uint32 i = 0; i < _handlers.size(); i++) + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo) + frame += ((VideoTrackHandler *)_handlers[i])->getCurFrame() + 1; - uint32 curFrameIndex = 0; - for (int32 i = 0; i < _tracks[_videoTrackIndex]->timeToSampleCount; i++) { - curFrameIndex += _tracks[_videoTrackIndex]->timeToSample[i].count; - if ((uint32)_curFrame < curFrameIndex) { - // Ok, now we have what duration this frame has. - return _tracks[_videoTrackIndex]->timeToSample[i].duration; - } - } - - // This should never occur - error ("Cannot find duration for frame %d", _curFrame); - return 0; -} - -Graphics::PixelFormat QuickTimeDecoder::getPixelFormat() const { - Codec *codec = findDefaultVideoCodec(); - - if (!codec) - return Graphics::PixelFormat::createFormatCLUT8(); - - return codec->getPixelFormat(); -} - -uint32 QuickTimeDecoder::findKeyFrame(uint32 frame) const { - for (int i = _tracks[_videoTrackIndex]->keyframeCount - 1; i >= 0; i--) - if (_tracks[_videoTrackIndex]->keyframes[i] <= frame) - return _tracks[_videoTrackIndex]->keyframes[i]; - - // If none found, we'll assume the requested frame is a key frame return frame; } -void QuickTimeDecoder::seekToFrame(uint32 frame) { - assert(_videoTrackIndex >= 0); - assert(frame < _tracks[_videoTrackIndex]->frameCount); - - // Stop all audio (for now) - stopAudio(); - - // Track down the keyframe - _curFrame = findKeyFrame(frame) - 1; - while (_curFrame < (int32)frame - 1) - decodeNextFrame(); - - // Map out the starting point - _nextFrameStartTime = 0; - uint32 curFrame = 0; - - for (int32 i = 0; i < _tracks[_videoTrackIndex]->timeToSampleCount && curFrame < frame; i++) { - for (int32 j = 0; j < _tracks[_videoTrackIndex]->timeToSample[i].count && curFrame < frame; j++) { - curFrame++; - _nextFrameStartTime += _tracks[_videoTrackIndex]->timeToSample[i].duration; - } - } - - // Adjust the video starting point - const Audio::Timestamp curVideoTime(0, _nextFrameStartTime, _tracks[_videoTrackIndex]->timeScale); - _startTime = g_system->getMillis() - curVideoTime.msecs(); - resetPauseStartTime(); - - // Adjust the audio starting point - if (_audioTrackIndex >= 0) { - _audioStartOffset = curVideoTime; - - // Seek to the new audio location - setAudioStreamPos(_audioStartOffset); - - // Restart the audio - startAudio(); - - // Pause the audio again if we're still paused - if (isPaused() && _audStream) - g_system->getMixer()->pauseHandle(_audHandle, true); - } -} - -void QuickTimeDecoder::seekToTime(Audio::Timestamp time) { - // Use makeQuickTimeStream() instead - if (_videoTrackIndex < 0) - error("Audio-only seeking not supported"); - - // Try to find the last frame that should have been decoded - uint32 frame = 0; - Audio::Timestamp totalDuration(0, _tracks[_videoTrackIndex]->timeScale); - bool done = false; +uint32 QuickTimeDecoder::getFrameCount() const { + uint32 count = 0; - for (int32 i = 0; i < _tracks[_videoTrackIndex]->timeToSampleCount && !done; i++) { - for (int32 j = 0; j < _tracks[_videoTrackIndex]->timeToSample[i].count; j++) { - totalDuration = totalDuration.addFrames(_tracks[_videoTrackIndex]->timeToSample[i].duration); - if (totalDuration > time) { - done = true; - break; - } - frame++; - } - } + for (uint32 i = 0; i < _handlers.size(); i++) + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo) + count += ((VideoTrackHandler *)_handlers[i])->getFrameCount(); - seekToFrame(frame); + return count; } void QuickTimeDecoder::startAudio() { @@ -224,98 +111,109 @@ void QuickTimeDecoder::pauseVideoIntern(bool pause) { g_system->getMixer()->pauseHandle(_audHandle, pause); } -Codec *QuickTimeDecoder::findDefaultVideoCodec() const { - if (_videoTrackIndex < 0 || _tracks[_videoTrackIndex]->sampleDescs.empty()) - return 0; +QuickTimeDecoder::VideoTrackHandler *QuickTimeDecoder::findNextVideoTrack() const { + VideoTrackHandler *bestTrack = 0; + int32 num; + uint32 bestTime = 0xffffffff; + + for (uint32 i = 0; i < _handlers.size(); i++) { + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo && !_handlers[i]->endOfTrack()) { + VideoTrackHandler *track = (VideoTrackHandler *)_handlers[i]; + uint32 time = track->getNextFrameStartTime(); + + if (time < bestTime) { + bestTime = time; + bestTrack = track; + num = i; + } + } + } - return ((VideoSampleDesc *)_tracks[_videoTrackIndex]->sampleDescs[0])->_videoCodec; + return bestTrack; } const Graphics::Surface *QuickTimeDecoder::decodeNextFrame() { - if (_videoTrackIndex < 0 || _curFrame >= (int32)getFrameCount() - 1) + if (!_nextVideoTrack) return 0; - if (_startTime == 0) - _startTime = g_system->getMillis(); - - _curFrame++; - _nextFrameStartTime += getFrameDuration(); - - // Update the audio while we're at it - updateAudioBuffer(); - - // Get the next packet - uint32 descId; - Common::SeekableReadStream *frameData = getNextFramePacket(descId); - - if (!frameData || !descId || descId > _tracks[_videoTrackIndex]->sampleDescs.size()) - return 0; + const Graphics::Surface *frame = _nextVideoTrack->decodeNextFrame(); - // Find which video description entry we want - VideoSampleDesc *entry = (VideoSampleDesc *)_tracks[_videoTrackIndex]->sampleDescs[descId - 1]; + if (!_setStartTime) { + _startTime = g_system->getMillis(); + _setStartTime = true; + } - if (!entry->_videoCodec) - return 0; + _nextVideoTrack = findNextVideoTrack(); + _needUpdate = false; - const Graphics::Surface *frame = entry->_videoCodec->decodeImage(frameData); - delete frameData; + // Update audio buffers too + // (needs to be done after we find the next track) + for (uint32 i = 0; i < _handlers.size(); i++) + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeAudio) + ((AudioTrackHandler *)_handlers[i])->updateBuffer(); - // Update the palette - if (entry->_videoCodec->containsPalette()) { - // The codec itself contains a palette - if (entry->_videoCodec->hasDirtyPalette()) { - _palette = entry->_videoCodec->getPalette(); - _dirtyPalette = true; - } - } else { - // Check if the video description has been updated - byte *palette = entry->_palette; - - if (palette != _palette) { - _palette = palette; - _dirtyPalette = true; - } + if (_scaledSurface) { + scaleSurface(frame, _scaledSurface, _scaleFactorX, _scaleFactorY); + return _scaledSurface; } - return scaleSurface(frame); + return frame; } -const Graphics::Surface *QuickTimeDecoder::scaleSurface(const Graphics::Surface *frame) { - if (getScaleFactorX() == 1 && getScaleFactorY() == 1) - return frame; +void QuickTimeDecoder::scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, Common::Rational scaleFactorX, Common::Rational scaleFactorY) { + assert(src && dst); - assert(_scaledSurface); - - for (int32 j = 0; j < _scaledSurface->h; j++) - for (int32 k = 0; k < _scaledSurface->w; k++) - memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr((k * getScaleFactorX()).toInt() , (j * getScaleFactorY()).toInt()), frame->format.bytesPerPixel); - - return _scaledSurface; + for (int32 j = 0; j < dst->h; j++) + for (int32 k = 0; k < dst->w; k++) + memcpy(dst->getBasePtr(k, j), src->getBasePtr((k * scaleFactorX).toInt() , (j * scaleFactorY).toInt()), src->format.bytesPerPixel); } bool QuickTimeDecoder::endOfVideo() const { - return (!_audStream || _audStream->endOfData()) && (!findDefaultVideoCodec() || SeekableVideoDecoder::endOfVideo()); + if (!isVideoLoaded()) + return true; + + for (uint32 i = 0; i < _handlers.size(); i++) + if (!_handlers[i]->endOfTrack()) + return false; + + return true; } uint32 QuickTimeDecoder::getElapsedTime() const { - if (_audStream) - return g_system->getMixer()->getSoundElapsedTime(_audHandle) + _audioStartOffset.msecs(); + // TODO: Convert to multi-track + if (_audStream) { + // Use the audio time if present and the audio track's time is less than the + // total length of the audio track. The audio track can end before the video + // track, so we need to fall back on the getMillis() time tracking in that + // case. + uint32 time = g_system->getMixer()->getSoundElapsedTime(_audHandle) + _audioStartOffset.msecs(); + if (time < _tracks[_audioTrackIndex]->mediaDuration * 1000 / _tracks[_audioTrackIndex]->timeScale) + return time; + } + // Just use time elapsed since the beginning return SeekableVideoDecoder::getElapsedTime(); } uint32 QuickTimeDecoder::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) + if (_needUpdate) return 0; - // Convert from the QuickTime rate base to 1000 - uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _tracks[_videoTrackIndex]->timeScale; - uint32 elapsedTime = getElapsedTime(); + if (_nextVideoTrack) { + uint32 nextFrameStartTime = _nextVideoTrack->getNextFrameStartTime(); - if (nextFrameStartTime <= elapsedTime) - return 0; + if (nextFrameStartTime == 0) + return 0; + + // TODO: Add support for rate modification + + uint32 elapsedTime = getElapsedTime(); + + if (elapsedTime < nextFrameStartTime) + return nextFrameStartTime - elapsedTime; + } - return nextFrameStartTime - elapsedTime; + return 0; } bool QuickTimeDecoder::loadFile(const Common::String &filename) { @@ -337,30 +235,49 @@ bool QuickTimeDecoder::loadStream(Common::SeekableReadStream *stream) { void QuickTimeDecoder::init() { Audio::QuickTimeAudioDecoder::init(); - _videoTrackIndex = -1; _startTime = 0; - - // Find video streams - for (uint32 i = 0; i < _tracks.size(); i++) - if (_tracks[i]->codecType == CODEC_TYPE_VIDEO && _videoTrackIndex < 0) - _videoTrackIndex = i; + _setStartTime = false; // Start the audio codec if we've got one that we can handle if (_audStream) { startAudio(); _audioStartOffset = Audio::Timestamp(0); + + // TODO: Support multiple audio tracks + // For now, just push back a handler for the first audio track + _handlers.push_back(new AudioTrackHandler(this, _tracks[_audioTrackIndex])); } - // Initialize video, if present - if (_videoTrackIndex >= 0) { - for (uint32 i = 0; i < _tracks[_videoTrackIndex]->sampleDescs.size(); i++) - ((VideoSampleDesc *)_tracks[_videoTrackIndex]->sampleDescs[i])->initCodec(); + // Initialize all the video tracks + for (uint32 i = 0; i < _tracks.size(); i++) { + if (_tracks[i]->codecType == CODEC_TYPE_VIDEO) { + for (uint32 j = 0; j < _tracks[i]->sampleDescs.size(); j++) + ((VideoSampleDesc *)_tracks[i]->sampleDescs[j])->initCodec(); - if (getScaleFactorX() != 1 || getScaleFactorY() != 1) { + _handlers.push_back(new VideoTrackHandler(this, _tracks[i])); + } + } + + // Prepare the first video track + _nextVideoTrack = findNextVideoTrack(); + + if (_nextVideoTrack) { + // Initialize the scaled surface + if (_scaleFactorX != 1 || _scaleFactorY != 1) { // We have to initialize the scaled surface _scaledSurface = new Graphics::Surface(); - _scaledSurface->create(getWidth(), getHeight(), getPixelFormat()); + _scaledSurface->create((_nextVideoTrack->getWidth() / _scaleFactorX).toInt(), + (_nextVideoTrack->getHeight() / _scaleFactorY).toInt(), getPixelFormat()); + _width = _scaledSurface->w; + _height = _scaledSurface->h; + } else { + _width = _nextVideoTrack->getWidth().toInt(); + _height = _nextVideoTrack->getHeight().toInt(); } + + _needUpdate = true; + } else { + _needUpdate = false; } } @@ -464,6 +381,7 @@ Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Track *tra void QuickTimeDecoder::close() { stopAudio(); + freeAllTrackHandlers(); if (_scaledSurface) { _scaledSurface->free(); @@ -471,93 +389,46 @@ void QuickTimeDecoder::close() { _scaledSurface = 0; } + _width = _height = 0; + Common::QuickTimeParser::close(); SeekableVideoDecoder::reset(); } -Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket(uint32 &descId) { - if (_videoTrackIndex < 0) - return NULL; - - // First, we have to track down which chunk holds the sample and which sample in the chunk contains the frame we are looking for. - int32 totalSampleCount = 0; - int32 sampleInChunk = 0; - int32 actualChunk = -1; - uint32 sampleToChunkIndex = 0; +void QuickTimeDecoder::freeAllTrackHandlers() { + for (uint32 i = 0; i < _handlers.size(); i++) + delete _handlers[i]; - for (uint32 i = 0; i < _tracks[_videoTrackIndex]->chunkCount; i++) { - if (sampleToChunkIndex < _tracks[_videoTrackIndex]->sampleToChunkCount && i >= _tracks[_videoTrackIndex]->sampleToChunk[sampleToChunkIndex].first) - sampleToChunkIndex++; - - totalSampleCount += _tracks[_videoTrackIndex]->sampleToChunk[sampleToChunkIndex - 1].count; - - if (totalSampleCount > getCurFrame()) { - actualChunk = i; - descId = _tracks[_videoTrackIndex]->sampleToChunk[sampleToChunkIndex - 1].id; - sampleInChunk = _tracks[_videoTrackIndex]->sampleToChunk[sampleToChunkIndex - 1].count - totalSampleCount + getCurFrame(); - break; - } - } - - if (actualChunk < 0) { - warning ("Could not find data for frame %d", getCurFrame()); - return NULL; - } - - // Next seek to that frame - _fd->seek(_tracks[_videoTrackIndex]->chunkOffsets[actualChunk]); - - // Then, if the chunk holds more than one frame, seek to where the frame we want is located - for (int32 i = getCurFrame() - sampleInChunk; i < getCurFrame(); i++) { - if (_tracks[_videoTrackIndex]->sampleSize != 0) - _fd->skip(_tracks[_videoTrackIndex]->sampleSize); - else - _fd->skip(_tracks[_videoTrackIndex]->sampleSizes[i]); - } + _handlers.clear(); +} - // Finally, read in the raw data for the frame - //printf ("Frame Data[%d]: Offset = %d, Size = %d\n", getCurFrame(), _fd->pos(), _tracks[_videoTrackIndex]->sampleSizes[getCurFrame()]); +void QuickTimeDecoder::seekToTime(Audio::Timestamp time) { + // Sets all tracks to this time + for (uint32 i = 0; i < _handlers.size(); i++) + _handlers[i]->seekToTime(time); - if (_tracks[_videoTrackIndex]->sampleSize != 0) - return _fd->readStream(_tracks[_videoTrackIndex]->sampleSize); + // Reset our start time + _startTime = g_system->getMillis() - time.msecs(); + _setStartTime = true; + resetPauseStartTime(); - return _fd->readStream(_tracks[_videoTrackIndex]->sampleSizes[getCurFrame()]); + // Reset the next video track too + _nextVideoTrack = findNextVideoTrack(); + _needUpdate = _nextVideoTrack != 0; } void QuickTimeDecoder::updateAudioBuffer() { - if (!_audStream) - return; - - uint32 numberOfChunksNeeded = 0; - - if (_videoTrackIndex < 0 || _curFrame == (int32)_tracks[_videoTrackIndex]->frameCount - 1) { - // If we have no video, there's nothing to base our buffer against - // However, one must ask why a QuickTimeDecoder is being used instead of the nice makeQuickTimeStream() function - - // If we're on the last frame, make sure all audio remaining is buffered - numberOfChunksNeeded = _tracks[_audioTrackIndex]->chunkCount; - } else { - Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0]; - - // Calculate the amount of chunks we need in memory until the next frame - uint32 timeToNextFrame = getTimeToNextFrame(); - uint32 timeFilled = 0; - uint32 curAudioChunk = _curAudioChunk - _audStream->numQueuedStreams(); - - for (; timeFilled < timeToNextFrame && curAudioChunk < _tracks[_audioTrackIndex]->chunkCount; numberOfChunksNeeded++, curAudioChunk++) { - uint32 sampleCount = entry->getAudioChunkSampleCount(curAudioChunk); - assert(sampleCount); - - timeFilled += sampleCount * 1000 / entry->_sampleRate; - } + // Updates the audio buffers for all audio tracks + for (uint32 i = 0; i < _handlers.size(); i++) + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeAudio) + ((AudioTrackHandler *)_handlers[i])->updateBuffer(); +} - // Add a couple extra to ensure we don't underrun - numberOfChunksNeeded += 3; - } +Graphics::PixelFormat QuickTimeDecoder::getPixelFormat() const { + if (_nextVideoTrack) + return _nextVideoTrack->getPixelFormat(); - // Keep three streams in buffer so that if/when the first two end, it goes right into the next - while (_audStream->numQueuedStreams() < numberOfChunksNeeded && _curAudioChunk < _tracks[_audioTrackIndex]->chunkCount) - queueNextAudioChunk(); + return Graphics::PixelFormat(); } QuickTimeDecoder::VideoSampleDesc::VideoSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) { @@ -612,4 +483,383 @@ void QuickTimeDecoder::VideoSampleDesc::initCodec() { } } +bool QuickTimeDecoder::endOfVideoTracks() const { + for (uint32 i = 0; i < _handlers.size(); i++) + if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo && !_handlers[i]->endOfTrack()) + return false; + + return true; +} + +QuickTimeDecoder::TrackHandler::TrackHandler(QuickTimeDecoder *decoder, Track *parent) : _decoder(decoder), _parent(parent), _fd(_decoder->_fd) { + _curEdit = 0; +} + +bool QuickTimeDecoder::TrackHandler::endOfTrack() { + // A track is over when we've finished going through all edits + return _curEdit == _parent->editCount; +} + +QuickTimeDecoder::AudioTrackHandler::AudioTrackHandler(QuickTimeDecoder *decoder, Track *parent) : TrackHandler(decoder, parent) { +} + +void QuickTimeDecoder::AudioTrackHandler::updateBuffer() { + if (!_decoder->_audStream) + return; + + uint32 numberOfChunksNeeded = 0; + + if (_decoder->endOfVideoTracks()) { + // If we have no video left (or no video), there's nothing to base our buffer against + numberOfChunksNeeded = _parent->chunkCount; + } else { + Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_parent->sampleDescs[0]; + + // Calculate the amount of chunks we need in memory until the next frame + uint32 timeToNextFrame = _decoder->getTimeToNextFrame(); + uint32 timeFilled = 0; + uint32 curAudioChunk = _decoder->_curAudioChunk - _decoder->_audStream->numQueuedStreams(); + + for (; timeFilled < timeToNextFrame && curAudioChunk < _parent->chunkCount; numberOfChunksNeeded++, curAudioChunk++) { + uint32 sampleCount = entry->getAudioChunkSampleCount(curAudioChunk); + assert(sampleCount); + + timeFilled += sampleCount * 1000 / entry->_sampleRate; + } + + // Add a couple extra to ensure we don't underrun + numberOfChunksNeeded += 3; + } + + // Keep three streams in buffer so that if/when the first two end, it goes right into the next + while (_decoder->_audStream->numQueuedStreams() < numberOfChunksNeeded && _decoder->_curAudioChunk < _parent->chunkCount) + _decoder->queueNextAudioChunk(); +} + +bool QuickTimeDecoder::AudioTrackHandler::endOfTrack() { + // TODO: Handle edits + return (_decoder->_curAudioChunk == _parent->chunkCount) && _decoder->_audStream->endOfData(); +} + +void QuickTimeDecoder::AudioTrackHandler::seekToTime(Audio::Timestamp time) { + if (_decoder->_audStream) { + // Stop all audio + _decoder->stopAudio(); + + _decoder->_audioStartOffset = time; + + // Seek to the new audio location + _decoder->setAudioStreamPos(_decoder->_audioStartOffset); + + // Restart the audio + _decoder->startAudio(); + + // Pause the audio again if we're still paused + if (_decoder->isPaused() && _decoder->_audStream) + g_system->getMixer()->pauseHandle(_decoder->_audHandle, true); + } +} + +QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent) : TrackHandler(decoder, parent) { + if (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1) { + _scaledSurface = new Graphics::Surface(); + _scaledSurface->create(getWidth().toInt(), getHeight().toInt(), getPixelFormat()); + } else { + _scaledSurface = 0; + } + + enterNewEditList(false); + + _holdNextFrameStartTime = false; + _curFrame = -1; + _durationOverride = -1; + +} + +QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { + if (_scaledSurface) { + _scaledSurface->free(); + delete _scaledSurface; + } +} + +const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() { + if (endOfTrack()) + return 0; + + const Graphics::Surface *frame = bufferNextFrame(); + + if (_holdNextFrameStartTime) { + // Don't set the next frame start time here; we just did a seek + _holdNextFrameStartTime = false; + } else if (_durationOverride >= 0) { + // Use our own duration from the edit list calculation + _nextFrameStartTime += _durationOverride; + _durationOverride = -1; + } else { + _nextFrameStartTime += getFrameDuration(); + } + + // Update the edit list, if applicable + // HACK: We're also accepting the time minus one because edit lists + // aren't as accurate as one would hope. + if (!endOfTrack() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { + _curEdit++; + + if (!endOfTrack()) + enterNewEditList(true); + } + + if (_scaledSurface) { + _decoder->scaleSurface(frame, _scaledSurface, _parent->scaleFactorX, _parent->scaleFactorY); + return _scaledSurface; + } + + return frame; +} + +void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) { + // Bypass all empty edit lists first + while (!endOfTrack() && _parent->editList[_curEdit].mediaTime == -1) + _curEdit++; + + if (endOfTrack()) + return; + + uint32 frameNum = 0; + bool done = false; + uint32 totalDuration = 0; + uint32 prevDuration = 0; + + // Track down where the mediaTime is in the media + for (int32 i = 0; i < _parent->timeToSampleCount && !done; i++) { + for (int32 j = 0; j < _parent->timeToSample[i].count; j++) { + if (totalDuration == (uint32)_parent->editList[_curEdit].mediaTime) { + done = true; + prevDuration = totalDuration; + break; + } else if (totalDuration > (uint32)_parent->editList[_curEdit].mediaTime) { + done = true; + frameNum--; + break; + } + + prevDuration = totalDuration; + totalDuration += _parent->timeToSample[i].duration; + frameNum++; + } + } + + if (bufferFrames) { + // Track down the keyframe + _curFrame = findKeyFrame(frameNum) - 1; + while (_curFrame < (int32)frameNum - 1) + bufferNextFrame(); + } else { + _curFrame = frameNum - 1; + } + + _nextFrameStartTime = getCurEditTimeOffset(); + + // Set an override for the duration since we came up in-between two frames + if (prevDuration != totalDuration) + _durationOverride = totalDuration - prevDuration; +} + +const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() { + _curFrame++; + + // Get the next packet + uint32 descId; + Common::SeekableReadStream *frameData = getNextFramePacket(descId); + + if (!frameData || !descId || descId > _parent->sampleDescs.size()) + return 0; + + // Find which video description entry we want + VideoSampleDesc *entry = (VideoSampleDesc *)_parent->sampleDescs[descId - 1]; + + if (!entry->_videoCodec) + return 0; + + const Graphics::Surface *frame = entry->_videoCodec->decodeImage(frameData); + delete frameData; + + // Update the palette + if (entry->_videoCodec->containsPalette()) { + // The codec itself contains a palette + if (entry->_videoCodec->hasDirtyPalette()) { + _decoder->_palette = entry->_videoCodec->getPalette(); + _decoder->_dirtyPalette = true; + } + } else { + // Check if the video description has been updated + byte *palette = entry->_palette; + + if (palette !=_decoder-> _palette) { + _decoder->_palette = palette; + _decoder->_dirtyPalette = true; + } + } + + return frame; +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getNextFrameStartTime() { + if (endOfTrack()) + return 0; + + // Convert to milliseconds so the tracks can be compared + return getRateAdjustedFrameTime() * 1000 / _parent->timeScale; +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getFrameCount() { + return _parent->frameCount; +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getFrameDuration() { + uint32 curFrameIndex = 0; + for (int32 i = 0; i < _parent->timeToSampleCount; i++) { + curFrameIndex += _parent->timeToSample[i].count; + if ((uint32)_curFrame < curFrameIndex) { + // Ok, now we have what duration this frame has. + return _parent->timeToSample[i].duration; + } + } + + // This should never occur + error("Cannot find duration for frame %d", _curFrame); + return 0; +} + +Common::SeekableReadStream *QuickTimeDecoder::VideoTrackHandler::getNextFramePacket(uint32 &descId) { + // First, we have to track down which chunk holds the sample and which sample in the chunk contains the frame we are looking for. + int32 totalSampleCount = 0; + int32 sampleInChunk = 0; + int32 actualChunk = -1; + uint32 sampleToChunkIndex = 0; + + for (uint32 i = 0; i < _parent->chunkCount; i++) { + if (sampleToChunkIndex < _parent->sampleToChunkCount && i >= _parent->sampleToChunk[sampleToChunkIndex].first) + sampleToChunkIndex++; + + totalSampleCount += _parent->sampleToChunk[sampleToChunkIndex - 1].count; + + if (totalSampleCount > _curFrame) { + actualChunk = i; + descId = _parent->sampleToChunk[sampleToChunkIndex - 1].id; + sampleInChunk = _parent->sampleToChunk[sampleToChunkIndex - 1].count - totalSampleCount + _curFrame; + break; + } + } + + if (actualChunk < 0) { + warning("Could not find data for frame %d", _curFrame); + return 0; + } + + // Next seek to that frame + _fd->seek(_parent->chunkOffsets[actualChunk]); + + // Then, if the chunk holds more than one frame, seek to where the frame we want is located + for (int32 i = _curFrame - sampleInChunk; i < _curFrame; i++) { + if (_parent->sampleSize != 0) + _fd->skip(_parent->sampleSize); + else + _fd->skip(_parent->sampleSizes[i]); + } + + // Finally, read in the raw data for the frame + //debug("Frame Data[%d]: Offset = %d, Size = %d", _curFrame, _fd->pos(), _parent->sampleSizes[_curFrame]); + + if (_parent->sampleSize != 0) + return _fd->readStream(_parent->sampleSize); + + return _fd->readStream(_parent->sampleSizes[_curFrame]); +} + +uint32 QuickTimeDecoder::VideoTrackHandler::findKeyFrame(uint32 frame) const { + for (int i = _parent->keyframeCount - 1; i >= 0; i--) + if (_parent->keyframes[i] <= frame) + return _parent->keyframes[i]; + + // If none found, we'll assume the requested frame is a key frame + return frame; +} + +void QuickTimeDecoder::VideoTrackHandler::seekToTime(Audio::Timestamp time) { + // First, figure out what edit we're in + time = time.convertToFramerate(_parent->timeScale); + + // Continue until we get to where we need to be + for (_curEdit = 0; !endOfTrack(); _curEdit++) + if ((uint32)time.totalNumberOfFrames() >= getCurEditTimeOffset() && (uint32)time.totalNumberOfFrames() < getCurEditTimeOffset() + getCurEditTrackDuration()) + break; + + // This track is done + if (endOfTrack()) + return; + + enterNewEditList(false); + + // One extra check for the end of a track + if (endOfTrack()) + return; + + // Now we're in the edit and need to figure out what frame we need + while (getRateAdjustedFrameTime() < (uint32)time.totalNumberOfFrames()) { + _curFrame++; + if (_durationOverride >= 0) { + _nextFrameStartTime += _durationOverride; + _durationOverride = -1; + } else { + _nextFrameStartTime += getFrameDuration(); + } + } + + // All that's left is to figure out what our starting time is going to be + // Compare the starting point for the frame to where we need to be + _holdNextFrameStartTime = getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames(); + + // If we went past the time, go back a frame + if (_holdNextFrameStartTime) + _curFrame--; + + // Handle the keyframe here + int32 destinationFrame = _curFrame + 1; + + assert(destinationFrame < (int32)_parent->frameCount); + _curFrame = findKeyFrame(destinationFrame) - 1; + while (_curFrame < destinationFrame - 1) + bufferNextFrame(); +} + +Common::Rational QuickTimeDecoder::VideoTrackHandler::getWidth() const { + return Common::Rational(_parent->width) / _parent->scaleFactorX; +} + +Common::Rational QuickTimeDecoder::VideoTrackHandler::getHeight() const { + return Common::Rational(_parent->height) / _parent->scaleFactorY; +} + +Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const { + return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat(); +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getRateAdjustedFrameTime() const { + // Figure out what time the next frame is at taking the edit list rate into account + uint32 convertedTime = (Common::Rational(_nextFrameStartTime - getCurEditTimeOffset()) / _parent->editList[_curEdit].mediaRate).toInt(); + return convertedTime + getCurEditTimeOffset(); +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTimeOffset() const { + // Need to convert to the track scale + return _parent->editList[_curEdit].timeOffset * _parent->timeScale / _decoder->_timeScale; +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTrackDuration() const { + // Need to convert to the track scale + return _parent->editList[_curEdit].trackDuration * _parent->timeScale / _decoder->_timeScale; +} + } // End of namespace Video diff --git a/video/qt_decoder.h b/video/qt_decoder.h index b51fd043e7..b2d7153f42 100644 --- a/video/qt_decoder.h +++ b/video/qt_decoder.h @@ -31,14 +31,14 @@ #ifndef VIDEO_QT_DECODER_H #define VIDEO_QT_DECODER_H +#include "audio/mixer.h" +#include "audio/decoders/quicktime_intern.h" #include "common/scummsys.h" #include "common/rational.h" +#include "graphics/pixelformat.h" #include "video/video_decoder.h" -#include "audio/mixer.h" -#include "audio/decoders/quicktime_intern.h" - namespace Common { class Rational; } @@ -63,13 +63,13 @@ public: * Returns the width of the video * @return the width of the video */ - uint16 getWidth() const; + uint16 getWidth() const { return _width; } /** * Returns the height of the video * @return the height of the video */ - uint16 getHeight() const; + uint16 getHeight() const { return _height; } /** * Returns the amount of frames in the video @@ -101,6 +101,8 @@ public: const byte *getPalette() { _dirtyPalette = false; return _palette; } bool hasDirtyPalette() const { return _dirtyPalette; } + int32 getCurFrame() const; + bool isVideoLoaded() const { return isOpen(); } const Graphics::Surface *decodeNextFrame(); bool endOfVideo() const; @@ -132,8 +134,6 @@ protected: Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format); private: - Common::SeekableReadStream *getNextFramePacket(uint32 &descId); - uint32 getFrameDuration(); void init(); void startAudio(); @@ -144,20 +144,108 @@ private: Audio::Timestamp _audioStartOffset; Codec *createCodec(uint32 codecTag, byte bitsPerPixel); - Codec *findDefaultVideoCodec() const; - uint32 _nextFrameStartTime; - int _videoTrackIndex; uint32 findKeyFrame(uint32 frame) const; bool _dirtyPalette; const byte *_palette; + bool _setStartTime; + bool _needUpdate; + + uint16 _width, _height; Graphics::Surface *_scaledSurface; - const Graphics::Surface *scaleSurface(const Graphics::Surface *frame); - Common::Rational getScaleFactorX() const; - Common::Rational getScaleFactorY() const; + void scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, + Common::Rational scaleFactorX, Common::Rational scaleFactorY); void pauseVideoIntern(bool pause); + bool endOfVideoTracks() const; + + // The TrackHandler is a class that wraps around a QuickTime Track + // and handles playback in this decoder class. + class TrackHandler { + public: + TrackHandler(QuickTimeDecoder *decoder, Track *parent); + virtual ~TrackHandler() {} + + enum TrackType { + kTrackTypeAudio, + kTrackTypeVideo + }; + + virtual TrackType getTrackType() const = 0; + + virtual void seekToTime(Audio::Timestamp time) = 0; + + virtual bool endOfTrack(); + + protected: + uint32 _curEdit; + QuickTimeDecoder *_decoder; + Common::SeekableReadStream *_fd; + Track *_parent; + }; + + // The AudioTrackHandler is currently just a wrapper around some + // QuickTimeDecoder functions. Eventually this can be made to + // handle multiple audio tracks, but I haven't seen a video with + // that yet. + class AudioTrackHandler : public TrackHandler { + public: + AudioTrackHandler(QuickTimeDecoder *decoder, Track *parent); + TrackType getTrackType() const { return kTrackTypeAudio; } + + void updateBuffer(); + void seekToTime(Audio::Timestamp time); + bool endOfTrack(); + }; + + // The VideoTrackHandler is the bridge between the time of playback + // and the media for the given track. It calculates when to start + // tracks and at what rate to play the media using the edit list. + class VideoTrackHandler : public TrackHandler { + public: + VideoTrackHandler(QuickTimeDecoder *decoder, Track *parent); + ~VideoTrackHandler(); + + TrackType getTrackType() const { return kTrackTypeVideo; } + + const Graphics::Surface *decodeNextFrame(); + + uint32 getNextFrameStartTime(); + + uint32 getFrameCount(); + + int32 getCurFrame() { return _curFrame; } + + Graphics::PixelFormat getPixelFormat() const; + + void seekToTime(Audio::Timestamp time); + + Common::Rational getWidth() const; + Common::Rational getHeight() const; + + private: + int32 _curFrame; + uint32 _nextFrameStartTime; + Graphics::Surface *_scaledSurface; + bool _holdNextFrameStartTime; + int32 _durationOverride; + + Common::SeekableReadStream *getNextFramePacket(uint32 &descId); + uint32 getFrameDuration(); + uint32 findKeyFrame(uint32 frame) const; + void enterNewEditList(bool bufferFrames); + const Graphics::Surface *bufferNextFrame(); + uint32 getRateAdjustedFrameTime() const; + uint32 getCurEditTimeOffset() const; + uint32 getCurEditTrackDuration() const; + }; + + Common::Array<TrackHandler *> _handlers; + VideoTrackHandler *_nextVideoTrack; + VideoTrackHandler *findNextVideoTrack() const; + + void freeAllTrackHandlers(); }; } // End of namespace Video |