aboutsummaryrefslogtreecommitdiff
path: root/engines/groovie
diff options
context:
space:
mode:
authorDreammaster2013-02-15 08:25:09 -0500
committerDreammaster2013-02-15 08:25:09 -0500
commitbb3285d933419b6bdefadc55a6e320855ff0dd27 (patch)
tree2df613c52f854c33cff660ed1b064e2996ed80bb /engines/groovie
parentd1a19a1d4c3e20b57250e73141d81e8d9b44a2a1 (diff)
parentadc338cd719179a94ff470b28e9584262d7b47e8 (diff)
downloadscummvm-rg350-bb3285d933419b6bdefadc55a6e320855ff0dd27.tar.gz
scummvm-rg350-bb3285d933419b6bdefadc55a6e320855ff0dd27.tar.bz2
scummvm-rg350-bb3285d933419b6bdefadc55a6e320855ff0dd27.zip
Merge branch 'master' into hopkins
Diffstat (limited to 'engines/groovie')
-rw-r--r--engines/groovie/detection.cpp130
-rw-r--r--engines/groovie/groovie.cpp28
-rw-r--r--engines/groovie/groovie.h10
-rw-r--r--engines/groovie/module.mk1
-rw-r--r--engines/groovie/music.cpp54
-rw-r--r--engines/groovie/music.h12
-rw-r--r--engines/groovie/stuffit.cpp537
-rw-r--r--engines/groovie/stuffit.h43
-rw-r--r--engines/groovie/vdx.cpp2
9 files changed, 790 insertions, 27 deletions
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 895686b5e0..65452f5cf3 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -25,9 +25,12 @@
#include "groovie/saveload.h"
#include "common/system.h"
+#include "common/translation.h"
namespace Groovie {
+#define GAMEOPTION_T7G_FAST_MOVIE_SPEED GUIO_GAMEOPTIONS1
+
static const PlainGameDescriptor groovieGames[] = {
// Games
{"t7g", "The 7th Guest"},
@@ -52,7 +55,7 @@ static const GroovieGameDescription gameDescriptions[] = {
"t7g", "",
AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659),
Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS,
- GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -63,7 +66,7 @@ static const GroovieGameDescription gameDescriptions[] = {
"t7g", "",
AD_ENTRY1s("T7GMac", "acdc4a58dd3f007f65e99b99d78e0bce", 1814029),
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK,
- GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -79,7 +82,7 @@ static const GroovieGameDescription gameDescriptions[] = {
"t7g", "",
AD_ENTRY1s("T7GMac", "6bdee8d0f9eef6d58d02fcd7deec3fb2", 1830783),
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK,
- GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -90,7 +93,7 @@ static const GroovieGameDescription gameDescriptions[] = {
"t7g", "",
AD_ENTRY1s("T7GMac", "0d595d4b44ae1814082938d051e5174e", 1830783),
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK,
- GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -106,7 +109,7 @@ static const GroovieGameDescription gameDescriptions[] = {
{ NULL, 0, NULL, 0}
},
Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS,
- GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -120,7 +123,7 @@ static const GroovieGameDescription gameDescriptions[] = {
{ NULL, 0, NULL, 0}
},
Common::EN_ANY, Common::kPlatformIOS, ADGF_NO_FLAGS,
- GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)
+ GUIO3(GUIO_NOMIDI, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED)
},
kGroovieT7G, 0
},
@@ -137,6 +140,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 1
},
+ // The 11th Hour Macintosh English
+ {
+ {
+ "11h", "",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 1
+ },
+
+ // The 11th Hour Macintosh English (Installed)
+ {
+ {
+ "11h", "Installed",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 1
+ },
+
// The 11th Hour DOS Demo English
{
{
@@ -159,6 +192,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 2
},
+ // The Making of The 11th Hour Macintosh English
+ {
+ {
+ "11h", "Making Of",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 2
+ },
+
+ // The Making of The 11th Hour Macintosh English (Installed)
+ {
+ {
+ "11h", "Making Of (Installed)",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 2
+ },
+
// Clandestiny Trailer DOS English
{
{
@@ -170,6 +233,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 3
},
+ // Clandestiny Trailer Macintosh English
+ {
+ {
+ "clandestiny", "Trailer",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 3
+ },
+
+ // Clandestiny Trailer Macintosh English (Installed)
+ {
+ {
+ "clandestiny", "Trailer (Installed)",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 3
+ },
+
// Clandestiny DOS English
{
{
@@ -207,9 +300,28 @@ static const GroovieGameDescription gameDescriptions[] = {
{AD_TABLE_END_MARKER, kGroovieT7G, 0}
};
+static const char *directoryGlobs[] = {
+ "MIDI",
+ 0
+};
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_T7G_FAST_MOVIE_SPEED,
+ {
+ _s("Fast movie speed"),
+ _s("Play movies at an increased speed"),
+ "fast_movie_speed",
+ false
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
class GroovieMetaEngine : public AdvancedMetaEngine {
public:
- GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames) {
+ GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames, optionsList) {
_singleid = "groovie";
// Use kADFlagUseExtraAsHint in order to distinguish the 11th hour from
@@ -222,6 +334,10 @@ public:
// replaced with an according explanation.
_flags = kADFlagUseExtraAsHint;
_guioptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT);
+
+ // Need MIDI directory to detect 11H Mac Installed
+ _maxScanDepth = 2;
+ _directoryGlobs = directoryGlobs;
}
const char *getName() const {
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 726e7cbede..5ade442742 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -30,6 +30,7 @@
#include "groovie/music.h"
#include "groovie/resource.h"
#include "groovie/roq.h"
+#include "groovie/stuffit.h"
#include "groovie/vdx.h"
#include "common/config-manager.h"
@@ -56,15 +57,11 @@ GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
SearchMan.addSubDirectoryMatching(gameDataDir, "groovie");
SearchMan.addSubDirectoryMatching(gameDataDir, "media");
SearchMan.addSubDirectoryMatching(gameDataDir, "system");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "MIDI");
_modeSpeed = kGroovieSpeedNormal;
- if (ConfMan.hasKey("t7g_speed")) {
- Common::String speed = ConfMan.get("t7g_speed");
- if (speed.equals("im_an_ios"))
- _modeSpeed = kGroovieSpeediOS;
- else if (speed.equals("tweaked"))
- _modeSpeed = kGroovieSpeedTweaked;
- }
+ if (ConfMan.hasKey("fast_movie_speed") && ConfMan.getBool("fast_movie_speed"))
+ _modeSpeed = kGroovieSpeedFast;
// Initialize the custom debug levels
DebugMan.addDebugChannel(kGroovieDebugAll, "All", "Debug everything");
@@ -93,6 +90,15 @@ GroovieEngine::~GroovieEngine() {
}
Common::Error GroovieEngine::run() {
+ if (_gameDescription->version == kGroovieV2 && getPlatform() == Common::kPlatformMacintosh) {
+ // Load the Mac installer with the lowest priority (in case the user has installed
+ // the game and has the MIDI folder present; faster to just load them)
+ Common::Archive *archive = createStuffItArchive("The 11th Hour Installer");
+
+ if (archive)
+ SearchMan.add("The 11th Hour Installer", archive);
+ }
+
_script = new Script(this, _gameDescription->version);
// Initialize the graphics
@@ -160,10 +166,10 @@ Common::Error GroovieEngine::run() {
// Create the music player
switch (getPlatform()) {
case Common::kPlatformMacintosh:
- // TODO: The 11th Hour Mac uses QuickTime MIDI files
- // Right now, since the XMIDI are present and it is still detected as
- // the DOS version, we don't have to do anything here.
- _musicPlayer = new MusicPlayerMac(this);
+ if (_gameDescription->version == kGroovieT7G)
+ _musicPlayer = new MusicPlayerMac_t7g(this);
+ else
+ _musicPlayer = new MusicPlayerMac_v2(this);
break;
case Common::kPlatformIOS:
_musicPlayer = new MusicPlayerIOS(this);
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
index df2f062757..79abc13b1c 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -72,10 +72,16 @@ enum DebugLevels {
// the current limitation is 32 debug levels (1 << 31 is the last one)
};
+/**
+ * This enum reflects the available movie speed settings:
+ * - Normal: play videos at a normal speed
+ * - Fast: play videos with audio at a fast speed. Videos without audio,
+ * like teeth animations, are played at their regular speed to avoid
+ * audio sync issues
+ */
enum GameSpeed {
kGroovieSpeedNormal,
- kGroovieSpeediOS,
- kGroovieSpeedTweaked
+ kGroovieSpeedFast
};
struct GroovieGameDescription;
diff --git a/engines/groovie/module.mk b/engines/groovie/module.mk
index 1e89ff66f5..b47eed912b 100644
--- a/engines/groovie/module.mk
+++ b/engines/groovie/module.mk
@@ -15,6 +15,7 @@ MODULE_OBJS := \
roq.o \
saveload.o \
script.o \
+ stuffit.o \
vdx.o
# This module can be built as a plugin
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index af929d439b..95637fc407 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -678,9 +678,9 @@ void MusicPlayerXMI::setTimbreMT(byte channel, const Timbre &timbre) {
}
-// MusicPlayerMac
+// MusicPlayerMac_t7g
-MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
+MusicPlayerMac_t7g::MusicPlayerMac_t7g(GroovieEngine *vm) : MusicPlayerMidi(vm) {
// Create the parser
_midiParser = MidiParser::createParser_SMF();
@@ -701,7 +701,7 @@ MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
assert(_vm->_macResFork);
}
-bool MusicPlayerMac::load(uint32 fileref, bool loop) {
+bool MusicPlayerMac_t7g::load(uint32 fileref, bool loop) {
debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref);
// First try for compressed MIDI
@@ -722,7 +722,7 @@ bool MusicPlayerMac::load(uint32 fileref, bool loop) {
return loadParser(file, loop);
}
-Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadStream *stream) {
+Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableReadStream *stream) {
// Initialize an output buffer of the given size
uint32 size = stream->readUint32BE();
byte *output = (byte *)malloc(size);
@@ -768,6 +768,52 @@ Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadS
return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
}
+// MusicPlayerMac_v2
+
+MusicPlayerMac_v2::MusicPlayerMac_v2(GroovieEngine *vm) : MusicPlayerMidi(vm) {
+ // Create the parser
+ _midiParser = MidiParser::createParser_QT();
+
+ // Create the driver
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = MidiDriver::createMidi(dev);
+ assert(_driver);
+
+ _driver->open(); // TODO: Handle return value != 0 (indicating an error)
+
+ // Set the parser's driver
+ _midiParser->setMidiDriver(this);
+
+ // Set the timer rate
+ _midiParser->setTimerRate(_driver->getBaseTempo());
+}
+
+bool MusicPlayerMac_v2::load(uint32 fileref, bool loop) {
+ debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref);
+
+ // Find correct filename
+ ResInfo info;
+ _vm->_resMan->getResInfo(fileref, info);
+ uint len = info.filename.size();
+ if (len < 4)
+ return false; // This shouldn't actually occur
+
+ // Remove the extension and add ".mov"
+ info.filename.deleteLastChar();
+ info.filename.deleteLastChar();
+ info.filename.deleteLastChar();
+ info.filename += "mov";
+
+ Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(info.filename);
+
+ if (!file) {
+ warning("Could not find file '%s'", info.filename.c_str());
+ return false;
+ }
+
+ return loadParser(file, loop);
+}
+
MusicPlayerIOS::MusicPlayerIOS(GroovieEngine *vm) : MusicPlayer(vm) {
vm->getTimerManager()->installTimerProc(&onTimer, 50 * 1000, this, "groovieMusic");
}
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index cc852aa8dc..92e9c8b487 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -150,9 +150,9 @@ private:
void setTimbreMT(byte channel, const Timbre &timbre);
};
-class MusicPlayerMac : public MusicPlayerMidi {
+class MusicPlayerMac_t7g : public MusicPlayerMidi {
public:
- MusicPlayerMac(GroovieEngine *vm);
+ MusicPlayerMac_t7g(GroovieEngine *vm);
protected:
bool load(uint32 fileref, bool loop);
@@ -161,6 +161,14 @@ private:
Common::SeekableReadStream *decompressMidi(Common::SeekableReadStream *stream);
};
+class MusicPlayerMac_v2 : public MusicPlayerMidi {
+public:
+ MusicPlayerMac_v2(GroovieEngine *vm);
+
+protected:
+ bool load(uint32 fileref, bool loop);
+};
+
class MusicPlayerIOS : public MusicPlayer {
public:
MusicPlayerIOS(GroovieEngine *vm);
diff --git a/engines/groovie/stuffit.cpp b/engines/groovie/stuffit.cpp
new file mode 100644
index 0000000000..37f12585e7
--- /dev/null
+++ b/engines/groovie/stuffit.cpp
@@ -0,0 +1,537 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Based on the StuffIt code in ResidualVM
+// StuffIt parsing based on http://code.google.com/p/theunarchiver/wiki/StuffItFormat
+// Compression 14 based on libxad (http://sourceforge.net/projects/libxad/)
+
+#include "groovie/stuffit.h"
+
+#include "common/archive.h"
+#include "common/bitstream.h"
+#include "common/debug.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/memstream.h"
+#include "common/substream.h"
+
+namespace Groovie {
+
+struct SIT14Data;
+
+class StuffItArchive : public Common::Archive {
+public:
+ StuffItArchive();
+ ~StuffItArchive();
+
+ bool open(const Common::String &filename);
+ void close();
+ bool isOpen() const { return _stream != 0; }
+
+ // Common::Archive API implementation
+ bool hasFile(const Common::String &name) const;
+ int listMembers(Common::ArchiveMemberList &list) const;
+ const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+private:
+ struct FileEntry {
+ byte compression;
+ uint32 uncompressedSize;
+ uint32 compressedSize;
+ uint32 offset;
+ };
+
+ Common::SeekableReadStream *_stream;
+
+ typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+ FileMap _map;
+
+ // Decompression Functions
+ Common::SeekableReadStream *decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const;
+
+ // Decompression Helpers
+ void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const;
+ void readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
+};
+
+StuffItArchive::StuffItArchive() : Common::Archive() {
+ _stream = 0;
+}
+
+StuffItArchive::~StuffItArchive() {
+ close();
+}
+
+// Some known values of StuffIt FourCC's
+// 11H Mac in particular uses ST46
+static const uint32 s_magicNumbers[] = {
+ MKTAG('S', 'I', 'T', '!'), MKTAG('S', 'T', '6', '5'), MKTAG('S', 'T', '5', '0'),
+ MKTAG('S', 'T', '6', '0'), MKTAG('S', 'T', 'i', 'n'), MKTAG('S', 'T', 'i', '2'),
+ MKTAG('S', 'T', 'i', '3'), MKTAG('S', 'T', 'i', '4'), MKTAG('S', 'T', '4', '6')
+};
+
+bool StuffItArchive::open(const Common::String &filename) {
+ close();
+
+ _stream = SearchMan.createReadStreamForMember(filename);
+
+ if (!_stream)
+ return false;
+
+ uint32 tag = _stream->readUint32BE();
+
+ // Check all the possible FourCC's
+ bool found = false;
+ for (int i = 0; i < ARRAYSIZE(s_magicNumbers); i++) {
+ if (tag == s_magicNumbers[i]) {
+ found = true;
+ break;
+ }
+ }
+
+ // Didn't find one, let's bail out
+ if (!found) {
+ close();
+ return false;
+ }
+
+ /* uint16 fileCount = */ _stream->readUint16BE();
+ /* uint32 archiveSize = */ _stream->readUint32BE();
+
+ // Some sort of second magic number
+ if (_stream->readUint32BE() != MKTAG('r', 'L', 'a', 'u')) {
+ close();
+ return false;
+ }
+
+ /* byte version = */ _stream->readByte(); // meaning not clear
+
+ _stream->skip(7); // unknown
+
+ while (_stream->pos() < _stream->size() && !_stream->eos()) {
+ byte resForkCompression = _stream->readByte();
+ byte dataForkCompression = _stream->readByte();
+
+ byte fileNameLength = _stream->readByte();
+ Common::String name;
+
+ for (byte i = 0; i < fileNameLength; i++)
+ name += (char)_stream->readByte();
+
+ // Skip remaining bytes
+ _stream->skip(63 - fileNameLength);
+
+ /* uint32 fileType = */ _stream->readUint32BE();
+ /* uint32 fileCreator = */ _stream->readUint32BE();
+ /* uint16 finderFlags = */ _stream->readUint16BE();
+ /* uint32 creationDate = */ _stream->readUint32BE();
+ /* uint32 modificationDate = */ _stream->readUint32BE();
+ uint32 resForkUncompressedSize = _stream->readUint32BE();
+ uint32 dataForkUncompressedSize = _stream->readUint32BE();
+ uint32 resForkCompressedSize = _stream->readUint32BE();
+ uint32 dataForkCompressedSize = _stream->readUint32BE();
+ /* uint16 resForkCRC = */ _stream->readUint16BE();
+ /* uint16 dataForkCRC = */ _stream->readUint16BE();
+ _stream->skip(6); // unknown
+ /* uint16 headerCRC = */ _stream->readUint16BE();
+
+ // Ignore directories for now
+ if (dataForkCompression == 32 || dataForkCompression == 33)
+ continue;
+
+ if (dataForkUncompressedSize != 0) {
+ // We have a data fork
+
+ FileEntry entry;
+ entry.compression = dataForkCompression;
+ entry.uncompressedSize = dataForkUncompressedSize;
+ entry.compressedSize = dataForkCompressedSize;
+ entry.offset = _stream->pos() + resForkCompressedSize;
+ _map[name] = entry;
+
+ debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+ }
+
+ if (resForkUncompressedSize != 0) {
+ // We have a resource fork
+
+ // Add a .rsrc extension so we know it's the resource fork
+ name += ".rsrc";
+
+ FileEntry entry;
+ entry.compression = resForkCompression;
+ entry.uncompressedSize = resForkUncompressedSize;
+ entry.compressedSize = resForkCompressedSize;
+ entry.offset = _stream->pos();
+ _map[name] = entry;
+
+ debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+ }
+
+ // Go to the next entry
+ _stream->skip(dataForkCompressedSize + resForkCompressedSize);
+ }
+
+ return true;
+}
+
+void StuffItArchive::close() {
+ delete _stream; _stream = 0;
+ _map.clear();
+}
+
+bool StuffItArchive::hasFile(const Common::String &name) const {
+ return _map.contains(name);
+}
+
+int StuffItArchive::listMembers(Common::ArchiveMemberList &list) const {
+ for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
+ list.push_back(getMember(it->_key));
+
+ return _map.size();
+}
+
+const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::String &name) const {
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *StuffItArchive::createReadStreamForMember(const Common::String &name) const {
+ if (!_stream || !_map.contains(name))
+ return 0;
+
+ const FileEntry &entry = _map[name];
+
+ if (entry.compression & 0xF0)
+ error("Unhandled StuffIt encryption");
+
+ Common::SeekableSubReadStream subStream(_stream, entry.offset, entry.offset + entry.compressedSize);
+
+ // We currently only support type 14 compression
+ switch (entry.compression) {
+ case 0: // Uncompressed
+ return subStream.readStream(subStream.size());
+ case 14: // Installer
+ return decompress14(&subStream, entry.uncompressedSize);
+ default:
+ error("Unhandled StuffIt compression %d", entry.compression);
+ }
+
+ return 0;
+}
+
+void StuffItArchive::update14(uint16 first, uint16 last, byte *code, uint16 *freq) const {
+ uint16 i, j;
+
+ while (last - first > 1) {
+ i = first;
+ j = last;
+
+ do {
+ while (++i < last && code[first] > code[i])
+ ;
+
+ while (--j > first && code[first] < code[j])
+ ;
+
+ if (j > i) {
+ SWAP(code[i], code[j]);
+ SWAP(freq[i], freq[j]);
+ }
+ } while (j > i);
+
+ if (first != j) {
+ SWAP(code[first], code[j]);
+ SWAP(freq[first], freq[j]);
+
+ i = j + 1;
+
+ if (last - i <= j - first) {
+ update14(i, last, code, freq);
+ last = j;
+ } else {
+ update14(first, j, code, freq);
+ first = i;
+ }
+ } else {
+ ++first;
+ }
+ }
+}
+
+struct SIT14Data {
+ byte code[308];
+ byte codecopy[308];
+ uint16 freq[308];
+ uint32 buff[308];
+
+ byte var1[52];
+ uint16 var2[52];
+ uint16 var3[75 * 2];
+
+ byte var4[76];
+ uint32 var5[75];
+ byte var6[1024];
+ uint16 var7[308 * 2];
+ byte var8[0x4000];
+
+ byte window[0x40000];
+};
+
+// Realign to a byte boundary
+#define ALIGN_BITS(b) \
+ if (b->pos() & 7) \
+ b->skip(8 - (b->pos() & 7))
+
+void StuffItArchive::readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const {
+ uint32 i, l, n;
+ uint32 k = bits->getBit();
+ uint32 j = bits->getBits(2) + 2;
+ uint32 o = bits->getBits(3) + 1;
+ uint32 size = 1 << j;
+ uint32 m = size - 1;
+ k = k ? (m - 1) : 0xFFFFFFFF;
+
+ if (bits->getBits(2) & 1) { // skip 1 bit!
+ // requirements for this call: dat->buff[32], dat->code[32], dat->freq[32*2]
+ readTree14(bits, dat, size, dat->freq);
+
+ for (i = 0; i < codesize; ) {
+ l = 0;
+
+ do {
+ l = dat->freq[l + bits->getBit()];
+ n = size << 1;
+ } while (n > l);
+
+ l -= n;
+
+ if (k != l) {
+ if (l == m) {
+ l = 0;
+
+ do {
+ l = dat->freq[l + bits->getBit()];
+ n = size << 1;
+ } while (n > l);
+
+ l += 3 - n;
+
+ while (l--) {
+ dat->code[i] = dat->code[i - 1];
+ ++i;
+ }
+ } else {
+ dat->code[i++] = l + o;
+ }
+ } else {
+ dat->code[i++] = 0;
+ }
+ }
+ } else {
+ for (i = 0; i < codesize; ) {
+ l = bits->getBits(j);
+
+ if (k != l) {
+ if (l == m) {
+ l = bits->getBits(j) + 3;
+
+ while (l--) {
+ dat->code[i] = dat->code[i - 1];
+ ++i;
+ }
+ } else {
+ dat->code[i++] = l+o;
+ }
+ } else {
+ dat->code[i++] = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < codesize; ++i) {
+ dat->codecopy[i] = dat->code[i];
+ dat->freq[i] = i;
+ }
+
+ update14(0, codesize, dat->codecopy, dat->freq);
+
+ for (i = 0; i < codesize && !dat->codecopy[i]; ++i)
+ ; // find first nonempty
+
+ for (j = 0; i < codesize; ++i, ++j) {
+ if (i)
+ j <<= (dat->codecopy[i] - dat->codecopy[i - 1]);
+
+ k = dat->codecopy[i]; m = 0;
+
+ for (l = j; k--; l >>= 1)
+ m = (m << 1) | (l & 1);
+
+ dat->buff[dat->freq[i]] = m;
+ }
+
+ for (i = 0; i < (uint32)codesize * 2; ++i)
+ result[i] = 0;
+
+ j = 2;
+
+ for (i = 0; i < codesize; ++i) {
+ l = 0;
+ m = dat->buff[i];
+
+ for (k = 0; k < dat->code[i]; ++k) {
+ l += (m & 1);
+
+ if (dat->code[i] - 1 <= (int32)k) {
+ result[l] = codesize * 2 + i;
+ } else {
+ if (!result[l]) {
+ result[l] = j;
+ j += 2;
+ }
+
+ l = result[l];
+ }
+
+ m >>= 1;
+ }
+ }
+
+ ALIGN_BITS(bits);
+}
+
+#define OUTPUT_VAL(x) \
+ out.writeByte(x); \
+ dat->window[j++] = x; \
+ j &= 0x3FFFF
+
+Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const {
+ byte *dst = (byte *)malloc(uncompressedSize);
+ Common::MemoryWriteStream out(dst, uncompressedSize);
+
+ Common::BitStream *bits = new Common::BitStream8LSB(src);
+
+ uint32 i, j, k, l, m, n;
+
+ SIT14Data *dat = new SIT14Data();
+
+ // initialization
+ for (i = k = 0; i < 52; ++i) {
+ dat->var2[i] = k;
+ k += (1 << (dat->var1[i] = ((i >= 4) ? ((i - 4) >> 2) : 0)));
+ }
+
+ for (i = 0; i < 4; ++i)
+ dat->var8[i] = i;
+
+ for (m = 1, l = 4; i < 0x4000; m <<= 1) // i is 4
+ for (n = l+4; l < n; ++l)
+ for (j = 0; j < m; ++j)
+ dat->var8[i++] = l;
+
+ for (i = 0, k = 1; i < 75; ++i) {
+ dat->var5[i] = k;
+ k += (1 << (dat->var4[i] = (i >= 3 ? ((i - 3) >> 2) : 0)));
+ }
+
+ for (i = 0; i < 4; ++i)
+ dat->var6[i] = i - 1;
+
+ for (m = 1, l = 3; i < 0x400; m <<= 1) // i is 4
+ for (n = l + 4; l < n; ++l)
+ for (j = 0; j < m; ++j)
+ dat->var6[i++] = l;
+
+ m = bits->getBits(16); // number of blocks
+ j = 0; // window position
+
+ while (m-- && !bits->eos()) {
+ bits->getBits(16); // skip crunched block size
+ bits->getBits(16);
+ n = bits->getBits(16); // number of uncrunched bytes
+ n |= bits->getBits(16) << 16;
+ readTree14(bits, dat, 308, dat->var7);
+ readTree14(bits, dat, 75, dat->var3);
+
+ while (n && !bits->eos()) {
+ for (i = 0; i < 616;)
+ i = dat->var7[i + bits->getBit()];
+
+ i -= 616;
+
+ if (i < 0x100) {
+ OUTPUT_VAL(i);
+ --n;
+ } else {
+ i -= 0x100;
+ k = dat->var2[i]+4;
+ i = dat->var1[i];
+
+ if (i)
+ k += bits->getBits(i);
+
+ for (i = 0; i < 150;)
+ i = dat->var3[i + bits->getBit()];
+
+ i -= 150;
+ l = dat->var5[i];
+ i = dat->var4[i];
+
+ if (i)
+ l += bits->getBits(i);
+
+ n -= k;
+ l = j + 0x40000 - l;
+
+ while (k--) {
+ l &= 0x3FFFF;
+ OUTPUT_VAL(dat->window[l]);
+ l++;
+ }
+ }
+ }
+
+ ALIGN_BITS(bits);
+ }
+
+ delete dat;
+ delete bits;
+
+ return new Common::MemoryReadStream(dst, uncompressedSize, DisposeAfterUse::YES);
+}
+
+#undef OUTPUT_VAL
+#undef ALIGN_BITS
+
+Common::Archive *createStuffItArchive(const Common::String &fileName) {
+ StuffItArchive *archive = new StuffItArchive();
+
+ if (!archive->open(fileName)) {
+ delete archive;
+ return 0;
+ }
+
+ return archive;
+}
+
+} // End of namespace Groovie
diff --git a/engines/groovie/stuffit.h b/engines/groovie/stuffit.h
new file mode 100644
index 0000000000..44f593dbea
--- /dev/null
+++ b/engines/groovie/stuffit.h
@@ -0,0 +1,43 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GROOVIE_STUFFIT_H
+#define GROOVIE_STUFFIT_H
+
+namespace Common {
+class Archive;
+class String;
+}
+
+namespace Groovie {
+
+/**
+ * This factory method creates an Archive instance corresponding to the content
+ * of the StuffIt compressed file.
+ *
+ * May return 0 in case of a failure.
+ */
+Common::Archive *createStuffItArchive(const Common::String &fileName);
+
+} // End of namespace Groovie
+
+#endif
diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp
index b3fcf462b2..8786e75488 100644
--- a/engines/groovie/vdx.cpp
+++ b/engines/groovie/vdx.cpp
@@ -88,7 +88,7 @@ uint16 VDXPlayer::loadInternal() {
// Enable highspeed if we're not obeying fps, and not marked as special
// This will be disabled in chunk audio if we're actually an audio vdx
- if ( _vm->_modeSpeed == kGroovieSpeediOS || (_vm->_modeSpeed == kGroovieSpeedTweaked && ((_flags & (1 << 15)) == 0)))
+ if (_vm->_modeSpeed == kGroovieSpeedFast && ((_flags & (1 << 15)) == 0))
setOverrideSpeed(true);
if (_flagOnePrev && !_flagOne && !_flagEight) {