Ticket #8937: groovie.patch

File groovie.patch, 167.0 KB (added by spookypeanut, 12 years ago)

groovie engine patch

  • configure

     
    8989add_engine cruise "Cinematique evo 2" no
    9090add_engine drascula "Drascula: The Vampire Strikes Back" yes
    9191add_engine gob "Gobli*ns" yes
     92add_engine groovie "Groovie" no
    9293add_engine igor "Igor: Objective Uikokahonia" no
    9394add_engine kyra "Legend of Kyrandia" yes
    9495add_engine lure "Lure of the Temptress" yes
  • engines/engines.mk

     
    4242MODULES += engines/gob
    4343endif
    4444
     45ifdef ENABLE_GROOVIE
     46DEFINES += -DENABLE_GROOVIE=$(ENABLE_GROOVIE)
     47MODULES += engines/groovie
     48endif
     49
    4550ifdef ENABLE_IGOR
    4651DEFINES += -DENABLE_IGOR=$(ENABLE_IGOR)
    4752MODULES += engines/igor
  • engines/groovie/lzss.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "common/stream.h"
     27
     28class LzssReadStream : public Common::ReadStream {
     29private:
     30        uint8 *_outLzssBufData;
     31        uint32 _size;
     32        uint32 _pos;
     33
     34        uint32 decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits);
     35
     36public:
     37        LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits);
     38        ~LzssReadStream();
     39
     40        bool eos() const;
     41        uint32 read(void *buf, uint32 size);
     42};
  • engines/groovie/graphics.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/graphics.h"
     28
     29namespace Groovie {
     30
     31GraphicsMan::GraphicsMan(GroovieEngine *vm) :
     32        _vm(vm), _changed(false), _fading(0) {
     33        // Create the game surfaces
     34        _foreground.create(640, 320, 1);
     35        _background.create(640, 320, 1);
     36}
     37
     38GraphicsMan::~GraphicsMan() {
     39        // Free the game surfaces
     40        _foreground.free();
     41        _background.free();
     42}
     43
     44void GraphicsMan::update() {
     45        if (_fading) {
     46                // Set the start time
     47                uint32 time = _vm->_system->getMillis() - _fadeStartTime;
     48
     49                // Scale the time
     50                int step = time / 4;
     51                if (step > 256) {
     52                        step = 256;
     53                }
     54
     55                if (_fading == 1) {
     56                        // Apply the fade in
     57                        applyFading(step);
     58                } else if (_fading == 2) {
     59                        // Apply the fade out
     60                        applyFading(256 - step);
     61
     62                        // Clear the buffer when ending the fade out
     63                        if (step == 256)
     64                                _foreground.fillRect(Common::Rect::Rect(640, 320), 0);
     65                }
     66
     67                // Check for the end
     68                if (step == 256) {
     69                        _fading = 0;
     70                }
     71        }
     72
     73        // Update the screen if needed and reset the status
     74        if (_changed) {
     75                _vm->_system->updateScreen();
     76                _changed = false;
     77        }
     78}
     79
     80void GraphicsMan::change() {
     81        _changed = true;
     82}
     83
     84void GraphicsMan::mergeFgAndBg() {
     85        uint32 i;
     86        byte *countf, *countb;
     87        countf = (byte *)_foreground.getBasePtr(0, 0);
     88        countb = (byte *)_background.getBasePtr(0, 0);
     89        for (i = 640 * 320; i; i--) {
     90                if (255 == *(countf)) {
     91                        *(countf) = *(countb);
     92                }
     93                countf++;
     94                countb++;
     95        }
     96}
     97
     98
     99void GraphicsMan::updateScreen(Graphics::Surface *source) {
     100        _vm->_system->copyRectToScreen((byte *)source->getBasePtr(0, 0), 640, 0, 80, 640, 320);
     101        change();
     102}
     103
     104bool GraphicsMan::isFading() {
     105        return _fading;
     106}
     107
     108void GraphicsMan::fadeIn(byte *pal) {
     109        // Set the start time
     110        _fadeStartTime = _vm->_system->getMillis();
     111
     112        // Copy the target palette
     113        for (int i = 0; i < 256; i++) {
     114                _paletteFull[(i * 4) + 0] = pal[(i * 3) + 0];
     115                _paletteFull[(i * 4) + 1] = pal[(i * 3) + 1];
     116                _paletteFull[(i * 4) + 2] = pal[(i * 3) + 2];
     117        }
     118
     119        // Apply a black palette right now
     120        applyFading(0);
     121
     122        // Set the current fading
     123        _fading = 1;
     124}
     125
     126void GraphicsMan::fadeOut() {
     127        // Set the start time
     128        _fadeStartTime = _vm->_system->getMillis();
     129
     130        // Get the current palette
     131        _vm->_system->grabPalette(_paletteFull, 0, 256);
     132
     133        // Set the current fading
     134        _fading = 2;
     135}
     136
     137void GraphicsMan::applyFading(int step) {
     138        // Calculate the fade factor for the given step
     139        int factorR = 256 - (256 - step) * 1;
     140        if (factorR <= 0) factorR = 0;
     141        int factorGB = 256 - (256 - step) * 2;
     142        if (factorGB <= 0) factorGB = 0;
     143
     144        // Calculate the new palette
     145        byte newpal[256 * 4];
     146        for (int i = 0; i < 256; i++) {
     147                newpal[(i * 4) + 0] = (_paletteFull[(i * 4) + 0] * factorR) / 256;
     148                newpal[(i * 4) + 1] = (_paletteFull[(i * 4) + 1] * factorGB) / 256;
     149                newpal[(i * 4) + 2] = (_paletteFull[(i * 4) + 2] * factorGB) / 256;
     150        }
     151
     152        // Set the screen palette
     153        _vm->_system->setPalette(newpal, 0, 256);
     154
     155        // Request a screen update
     156        change();
     157}
     158
     159} // End of Groovie namespace
  • engines/groovie/roq.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/jpeg.h"
     28#include "groovie/roq.h"
     29
     30#include "sound/mixer.h"
     31
     32namespace Groovie {
     33
     34ROQPlayer::ROQPlayer(GroovieEngine *vm) :
     35        VideoPlayer(vm) {
     36}
     37
     38ROQPlayer::~ROQPlayer() {
     39}
     40
     41uint16 ROQPlayer::loadInternal() {
     42        // Begin reading the file
     43        debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Loading video");
     44
     45        // Read the file header
     46        ROQBlockHeader blockHeader;
     47        if (!readBlockHeader(blockHeader)) {
     48                return 0;
     49        }
     50        if (blockHeader.type != 0x1084 || blockHeader.size != 0 || blockHeader.param != 0) {
     51                return 0;
     52        }
     53
     54        // Hardcoded FPS
     55        return 25;
     56}
     57
     58bool ROQPlayer::playFrameInternal() {
     59        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Playing frame");
     60
     61        // Process the needed blocks until the next video frame
     62        bool endframe = false;
     63        while (!endframe && !_file->eos()) {
     64                endframe = processBlock();
     65        }
     66
     67        // Wait until the current frame can be shown
     68        waitFrame();
     69
     70        // Update the screen
     71        _syst->updateScreen();
     72
     73        // Return whether the video has ended
     74        return _file->eos();
     75}
     76
     77bool ROQPlayer::readBlockHeader(ROQBlockHeader &blockHeader) {
     78        if (_file->eos()) {
     79                return false;
     80        } else {
     81                blockHeader.type = _file->readUint16LE();
     82                blockHeader.size = _file->readUint32LE();
     83                blockHeader.param = _file->readUint16LE();
     84
     85                debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block type = 0x%02X", blockHeader.type);
     86                debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block size = 0x%08X", blockHeader.size);
     87                debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block param = 0x%04X", blockHeader.param);
     88
     89                return true;
     90        }
     91}
     92
     93bool ROQPlayer::processBlock() {
     94        // Read the header of the block
     95        ROQBlockHeader blockHeader;
     96        if (!readBlockHeader(blockHeader)) {
     97                return true;
     98        }
     99
     100        bool ok = true;
     101        bool endframe = false;
     102        switch (blockHeader.type) {
     103        case 0x1001: // Video info
     104                ok = processBlockInfo(blockHeader);
     105                break;
     106        case 0x1002: // Quad codebook definition
     107                ok = processBlockQuadCodebook(blockHeader);
     108                break;
     109        case 0x1011: // Quad vector quantised video frame
     110                ok = processBlockQuadVector(blockHeader);
     111                endframe = true;
     112                break;
     113        case 0x1012: // Still image (JPEG)
     114                ok = processBlockStill(blockHeader);
     115                endframe = true;
     116                break;
     117        case 0x1013: // Hang
     118                //warning("Groovie::ROQ: Hang block (skipped)");
     119                break;
     120        case 0x1020: // Mono sound samples
     121                ok = processBlockSoundMono(blockHeader);
     122                break;
     123        case 0x1021: // Stereo sound samples
     124                ok = processBlockSoundStereo(blockHeader);
     125                break;
     126        case 0x1030: // Audio container
     127                ok = processBlockAudioContainer(blockHeader);
     128                break;
     129        default:
     130                error("Groovie::ROQ: Unknown block type: 0x%04X", blockHeader.type);
     131                ok = false;
     132        }
     133
     134        // End the frame when the graphics have been modified or when there's an error
     135        return endframe || !ok;
     136}
     137
     138bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) {
     139        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing info block");
     140
     141        // Verify the block header
     142        if (blockHeader.type != 0x1001 || blockHeader.size != 8 || blockHeader.param != 0) {
     143                return false;
     144        }
     145
     146        uint16 tmp;
     147        tmp = _file->readUint16LE();
     148        printf("w = %d\n", tmp);
     149        if (tmp != 640) {
     150                return false;
     151        }
     152        tmp = _file->readUint16LE();
     153        printf("h = %d\n", tmp);
     154        if (tmp != 320) {
     155                return false;
     156        }
     157        tmp = _file->readUint16LE();
     158        printf("unk1 = %d\n", tmp);
     159        if (tmp != 8) {
     160                return false;
     161        }
     162        tmp = _file->readUint16LE();
     163        printf("unk2 = %d\n", tmp);
     164        if (tmp != 4) {
     165                return false;
     166        }
     167        return true;
     168}
     169
     170bool ROQPlayer::processBlockQuadCodebook(ROQBlockHeader &blockHeader) {
     171        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad codebook block");
     172
     173        // Get the number of 2x2 pixel blocks
     174        _num2blocks = blockHeader.param >> 8;
     175        if (_num2blocks == 0) {
     176                _num2blocks = 256;
     177        }
     178
     179        // Get the number of 4x4 pixel blocks
     180        _num4blocks = blockHeader.param & 0xFF;
     181        if (_num4blocks == 0 && (blockHeader.size > (uint32)_num2blocks * 6)) {
     182                _num4blocks = 256;
     183        }
     184
     185        _file->skip(_num2blocks * 6);
     186        _file->skip(_num4blocks * 4);
     187
     188        return true;
     189}
     190
     191bool ROQPlayer::processBlockQuadVector(ROQBlockHeader &blockHeader) {
     192        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector block");
     193        _file->skip(blockHeader.size);
     194        return true;
     195
     196        // Get the mean motion vectors
     197        byte Mx = blockHeader.param >> 8;
     198        byte My = blockHeader.param & 0xFF;
     199
     200int32 ends =_file->pos() + blockHeader.size;
     201        int numblocks = (640 / 8) * (320 / 8);
     202        for (int j = 0; j < numblocks && ends > _file->pos(); j++) {
     203                printf("doing block %d/%d\n", j, numblocks);
     204                uint16 codingType = _file->readUint16LE();
     205                for (int i = 0; i < 8; i++) {
     206                        switch (codingType >> 14) {
     207                        case 0: // MOT: Skip block
     208                                //printf("coding type 0\n");
     209                                break;
     210                        case 1: { // FCC: Copy an existing block
     211                                //printf("coding type 1\n");
     212                                byte argument = _file->readByte();
     213                                byte Dx = Mx + (argument >> 4);
     214                                byte Dy = My + (argument & 0x0F);
     215                                // Dx = X + 8 - (argument >> 4) - Mx
     216                                // Dy = Y + 8 - (argument & 0x0F) - My
     217                                break;
     218                        }
     219                        case 2: { // SLD: Quad vector quantisation
     220                                //printf("coding type 2\n");
     221                                byte argument = _file->readByte();
     222                                if (argument > _num4blocks) {
     223                                        //error("invalid 4x4 block %d of %d", argument, _num4blocks);
     224                                }
     225                                // Upsample the 4x4 pixel block
     226                                break;
     227                        }
     228                        case 3: // CCC:
     229                                //printf("coding type 3:\n");
     230                                processBlockQuadVectorSub(blockHeader);
     231                                break;
     232                        }
     233                        codingType <<= 2;
     234                }
     235        }
     236printf("Should have ended at %d, and has ended at %d\n", ends, _file->pos());
     237        return true;
     238}
     239
     240bool ROQPlayer::processBlockQuadVectorSub(ROQBlockHeader &blockHeader) {
     241        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
     242
     243        // Get the mean motion vectors
     244        byte Mx = blockHeader.param >> 8;
     245        byte My = blockHeader.param & 0xFF;
     246
     247        uint16 codingType = _file->readUint16LE();
     248        for (int i = 0; i < 4; i++) {
     249                switch (codingType >> 14) {
     250                case 0: // MOT: Skip block
     251                        //printf("coding type 0\n");
     252                        break;
     253                case 1: { // FCC: Copy an existing block
     254                        //printf("coding type 1\n");
     255                        byte argument = _file->readByte();
     256                        byte Dx = Mx + (argument >> 4);
     257                        byte Dy = My + (argument & 0x0F);
     258                        // Dx = X + 8 - (argument >> 4) - Mx
     259                        // Dy = Y + 8 - (argument & 0x0F) - My
     260                        break;
     261                }
     262                case 2: { // SLD: Quad vector quantisation
     263                        //printf("coding type 2\n");
     264                        byte argument = _file->readByte();
     265                        if (argument > _num2blocks) {
     266                                //error("invalid 2x2 block: %d of %d", argument, _num2blocks);
     267                        }
     268                        break;
     269                }
     270                case 3:
     271                        //printf("coding type 3\n");
     272                        _file->readByte();
     273                        _file->readByte();
     274                        _file->readByte();
     275                        _file->readByte();
     276                        break;
     277                }
     278                codingType <<= 2;
     279        }
     280        return true;
     281}
     282
     283bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
     284        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block");
     285        Common::ReadStream *jpegData = new Common::SubReadStream(_file, blockHeader.size);
     286        JPEG jpegFrame;
     287        jpegFrame.read(jpegData);
     288        /*
     289        Common::File save;
     290        save.open("dump.jpg", Common::File::kFileWriteMode);
     291        save.write(data, blockHeader.size);
     292        save.close();
     293        */
     294        error("JPEG!");
     295        return true;
     296}
     297
     298bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) {
     299        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing mono sound block");
     300
     301        // Verify the block header
     302        if (blockHeader.type != 0x1020) {
     303                return false;
     304        }
     305
     306        // Initialize the audio stream if needed
     307        if (!_audioStream) {
     308                byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE;
     309#ifdef SCUMM_LITTLE_ENDIAN
     310                flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
     311#endif
     312                _audioStream = Audio::makeAppendableAudioStream(22050, flags);
     313                Audio::SoundHandle sound_handle;
     314                ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
     315        }
     316
     317        // Create the audio buffer
     318        int16 *buffer = new int16[blockHeader.size];
     319
     320        // Initialize the prediction with the block parameter
     321        int16 prediction = blockHeader.param ^ 0x8000;
     322
     323        // Process the data
     324        for (uint16 i = 0; i < blockHeader.size; i++) {
     325                int16 data = _file->readByte();
     326                if (data < 0x80) {
     327                        prediction += data * data;
     328                } else {
     329                        data -= 0x80;
     330                        prediction -= data * data;
     331                }
     332                buffer[i] = prediction;
     333        }
     334
     335        // Queue the read buffer
     336        _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
     337
     338        return true;
     339}
     340
     341bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) {
     342        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing stereo sound block");
     343
     344        // Verify the block header
     345        if (blockHeader.type != 0x1021) {
     346                return false;
     347        }
     348
     349        // Initialize the audio stream if needed
     350        if (!_audioStream) {
     351                byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_STEREO;
     352#ifdef SCUMM_LITTLE_ENDIAN
     353                flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
     354#endif
     355                _audioStream = Audio::makeAppendableAudioStream(22050, flags);
     356                Audio::SoundHandle sound_handle;
     357                ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
     358        }
     359
     360        // Create the audio buffer
     361        int16 *buffer = new int16[blockHeader.size];
     362
     363        // Initialize the prediction with the block parameter
     364        int16 predictionLeft = (blockHeader.param & 0xFF00) ^ 0x8000;
     365        int16 predictionRight = (blockHeader.param << 8) ^ 0x8000;
     366        bool left = true;
     367
     368        // Process the data
     369        for (uint16 i = 0; i < blockHeader.size; i++) {
     370                int16 data = _file->readByte();
     371                if (left) {
     372                        if (data < 0x80) {
     373                                predictionLeft += data * data;
     374                        } else {
     375                                data -= 0x80;
     376                                predictionLeft -= data * data;
     377                        }
     378                        buffer[i] = predictionLeft;
     379                } else {
     380                        if (data < 0x80) {
     381                                predictionRight += data * data;
     382                        } else {
     383                                data -= 0x80;
     384                                predictionRight -= data * data;
     385                        }
     386                        buffer[i] = predictionRight;
     387                }
     388                left = !left;
     389        }
     390
     391        // Queue the read buffer
     392        _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
     393
     394        return true;
     395}
     396
     397bool ROQPlayer::processBlockAudioContainer(ROQBlockHeader &blockHeader) {
     398        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing audio container block: 0x%04X", blockHeader.param);
     399        return true;
     400}
     401
     402} // End of Groovie namespace
  • engines/groovie/vdx.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/lzss.h"
     28#include "groovie/vdx.h"
     29
     30#include "sound/mixer.h"
     31
     32#define TILE_SIZE 4             // Size of each tile on the image: only ever seen 4 so far
     33#define VDX_IDENT 0x9267        // 37479
     34
     35namespace Groovie {
     36
     37VDXPlayer::VDXPlayer(GroovieEngine *vm) :
     38        VideoPlayer(vm), _origX(0), _origY(0), _flagOnePrev(false),
     39        _fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background) {
     40}
     41
     42VDXPlayer::~VDXPlayer() {
     43        //delete _audioStream;
     44}
     45
     46void VDXPlayer::setOrigin(int16 x, int16 y) {
     47        _origX = x;
     48        _origY = y;
     49}
     50
     51uint16 VDXPlayer::loadInternal() {
     52        uint32 engine_level = kGroovieDebugVideo | kGroovieDebugAll;
     53        if ((gDebugLevel == 11) || (Common::getEnabledSpecialDebugLevels() & engine_level)) {
     54                int8 i;
     55                debugN(1, "Groovie::VDX: New VDX: bitflags are ");
     56                for (i = 11; i >= 0; i--) {
     57                        debugN(1, "%d", _flags & (1 << i)? 1 : 0);
     58                        if (i % 4 == 0) {
     59                                debugN(1, " ");
     60                        }
     61                }
     62                debug(1, " ");
     63        }
     64        // Flags:
     65        // - 1 Puzzle piece? Skip palette, don't redraw full screen, draw still to b/ack buffer
     66        // - 2 Transparent colour is 0xFF
     67        // - 5 Skip still chunks
     68        // - 7
     69        // - 8 Just show the first frame
     70        // - 9 Start a palette fade in
     71        _flagZero =             ((_flags & (1 << 0)) != 0);
     72        _flagOne =              ((_flags & (1 << 1)) != 0);
     73        _flag2Byte =            (_flags & (1 << 2)) ? 0xFF : 0x00;
     74        _flagThree =            ((_flags & (1 << 3)) != 0);
     75        _flagFour =             ((_flags & (1 << 4)) != 0);
     76        _flagFive =             ((_flags & (1 << 5)) != 0);
     77        _flagSix =              ((_flags & (1 << 6)) != 0);
     78        _flagSeven =            ((_flags & (1 << 7)) != 0);
     79        _flagEight =            ((_flags & (1 << 8)) != 0);
     80        _flagNine =             ((_flags & (1 << 9)) != 0);
     81
     82        if (_flagOnePrev && !_flagOne && !_flagEight) {
     83                _flagSeven = true;
     84        }
     85
     86        // Save _flagOne for the next video
     87        _flagOnePrev = _flagOne;
     88
     89        //_flagTransparent =    _flagOne;
     90        _flagFirstFrame =       _flagEight;
     91        //_flagSkipPalette =    _flagSeven;
     92        _flagSkipPalette =      false;
     93        //_flagSkipStill =      _flagFive || _flagSeven;
     94        //_flagUpdateStill =    _flagNine || _flagSix;
     95
     96        // Begin reading the file
     97        debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Playing video");
     98
     99        if (_file->readUint16LE() != VDX_IDENT) {
     100                error("Groovie::VDX: This does not appear to be a 7th guest vxd file");
     101                return 0;
     102        } else {
     103                debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: VDX file identified correctly");
     104        }
     105
     106        uint16 tmp;
     107
     108        // Skip unknown data: 6 bytes, ref Martine
     109        tmp = _file->readUint16LE();
     110        debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine1 = 0x%04X", tmp);
     111        tmp = _file->readUint16LE();
     112        debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine2 = 0x%04X", tmp);
     113        tmp = _file->readUint16LE();
     114        debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine3 (FPS?) = %d", tmp);
     115
     116        return tmp;
     117}
     118
     119bool VDXPlayer::playFrameInternal() {
     120        byte currRes = 0x80;
     121        while (!_file->eos() && currRes == 0x80) {
     122                currRes = _file->readByte();
     123
     124                // Skip unknown data: 1 byte, ref Edward
     125                byte tmp = _file->readByte();
     126                debugC(5, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Edward = 0x%04X", tmp);
     127
     128                uint32 compSize = _file->readUint32LE();
     129                uint8 lengthmask = _file->readByte();
     130                uint8 lengthbits = _file->readByte();
     131
     132                // Read the chunk data and decompress if needed
     133                Common::ReadStream *vdxData = new Common::SubReadStream(_file, compSize);
     134                if (lengthmask && lengthbits) {
     135                        Common::ReadStream *decompData = new LzssReadStream(vdxData, lengthmask, lengthbits);
     136                        delete vdxData;
     137                        vdxData = decompData;
     138                }
     139
     140                // Use the current chunk
     141                switch (currRes) {
     142                        case 0x00:
     143                                debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Replay frame");
     144                                break;
     145                        case 0x20:
     146                                debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Still frame");
     147                                getStill(vdxData);
     148                                break;
     149                        case 0x25:
     150                                debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Animation frame");
     151                                getDelta(vdxData);
     152                                break;
     153                        case 0x80:
     154                                debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Sound resource");
     155                                chunkSound(vdxData);
     156                                break;
     157                        default:
     158                                error("Groovie::VDX: Invalid resource type: %d", currRes);
     159                }
     160                delete vdxData;
     161        }
     162
     163        // Wait until the current frame can be shown
     164        waitFrame();
     165
     166        // TODO: Move it to a better place
     167        // Update the screen
     168        if (currRes == 0x25) {
     169                //if (_flagSeven) {
     170                        //_vm->_graphicsMan->mergeFgAndBg();
     171                //}
     172                _vm->_graphicsMan->updateScreen(_bg);
     173        }
     174
     175        // Report the end of the video if we reached the end of the file or if we
     176        // just wanted to play one frame.
     177        return _file->eos() || _flagFirstFrame;
     178}
     179
     180static const uint16 vdxBlockMapLookup[] = {
     1810xc800, 0xec80, 0xfec8, 0xffec, 0xfffe, 0x3100, 0x7310, 0xf731, 0xff73, 0xfff7, 0x6c80, 0x36c8, 0x136c, 0x6310, 0xc631, 0x8c63,
     1820xf000, 0xff00, 0xfff0, 0x1111, 0x3333, 0x7777, 0x6666, 0xcccc, 0x0ff0, 0x00ff, 0xffcc, 0x0076, 0xff33, 0x0ee6, 0xccff, 0x6770,
     1830x33ff, 0x6ee0, 0x4800, 0x2480, 0x1248, 0x0024, 0x0012, 0x2100, 0x4210, 0x8421, 0x0042, 0x0084, 0xf888, 0x0044, 0x0032, 0x111f,
     1840x22e0, 0x4c00, 0x888f, 0x4470, 0x2300, 0xf111, 0x0e22, 0x00c4, 0xf33f, 0xfccf, 0xff99, 0x99ff, 0x4444, 0x2222, 0xccee, 0x7733,
     1850x00f8, 0x00f1, 0x00bb, 0x0cdd, 0x0f0f, 0x0f88, 0x13f1, 0x19b3, 0x1f80, 0x226f, 0x27ec, 0x3077, 0x3267, 0x37e4, 0x38e3, 0x3f90,
     1860x44cf, 0x4cd9, 0x4c99, 0x5555, 0x603f, 0x6077, 0x6237, 0x64c9, 0x64cd, 0x6cd9, 0x70ef, 0x0f00, 0x00f0, 0x0000, 0x4444, 0x2222
     187};
     188
     189void VDXPlayer::getDelta(Common::ReadStream *in) {
     190        uint16 j, k, l;
     191        uint32 offset;
     192        uint8 currOpCode, param1, param2, param3;
     193
     194        // Get the size of the local palette
     195        j = in->readUint16LE();
     196
     197        // Load the palette if it isn't empty
     198        if (j) {
     199                uint16 palBitField[16];
     200                int flag = 1, palIndex;
     201
     202                // Load the bit field
     203                for (l = 0; l < 16; l++) {
     204                        palBitField[l] = in->readUint16LE();
     205                }
     206
     207                // Load the actual palette
     208                for (l = 0; l < 16; l++) {
     209                        flag = 1 << 15;
     210                        for (j = 0; j < 16; j++) {
     211                                palIndex = (l * 16) + j;
     212
     213                                if (flag & palBitField[l]) {
     214                                        for (k = 0; k < 3; k++) {
     215                                                _palBuf[(palIndex * 3) + k] = in->readByte();
     216                                        }
     217                                }
     218                                flag = flag >> 1;
     219                        }
     220                }
     221
     222                // Apply the palette
     223                if (!_flagSix && !_flagSeven) {
     224                        setPalette(_palBuf);
     225                }
     226        }
     227        currOpCode = in->readByte();
     228
     229        /* j now becomes the current block line we're dealing with */
     230        j = 0;
     231        offset = 0;
     232        while (!in->eos()) {
     233                byte colours[16];
     234                if (currOpCode < 0x60) {
     235                        param1 = in->readByte();
     236                        param2 = in->readByte();
     237                        expandColourMap(colours, vdxBlockMapLookup[currOpCode], param1, param2);
     238                        decodeBlockDelta(offset, colours, 640);
     239                        offset += TILE_SIZE;
     240                } else if (currOpCode > 0x7f) {
     241                        param1 = in->readByte();
     242                        param2 = in->readByte();
     243                        param3 = in->readByte();
     244                        expandColourMap(colours, (param1 << 8) + currOpCode, param2, param3);
     245                        decodeBlockDelta(offset, colours, 640);
     246                        offset += TILE_SIZE;
     247                } else switch (currOpCode) {
     248                        case 0x60: /* Fill tile with the 16 colours given as parameters */
     249                                for (l = 0; l < 16; l++) {
     250                                        colours[l] = in->readByte();
     251                                }
     252                                decodeBlockDelta(offset, colours, 640);
     253                                offset += TILE_SIZE;
     254                                break;
     255                        case 0x61: /* Skip to the end of this line, next block is start of next */
     256                                /* Note this is used at the end of EVERY line */
     257                                j++;
     258                                offset = j * TILE_SIZE * 640;
     259                                break;
     260                        case 0x62:
     261                        case 0x63:
     262                        case 0x64:
     263                        case 0x65:
     264                        case 0x66:
     265                        case 0x67:
     266                        case 0x68:
     267                        case 0x69:
     268                        case 0x6a:
     269                        case 0x6b: /* Skip next param1 blocks (within line) */
     270                                offset += (currOpCode - 0x62) * TILE_SIZE;
     271                                break;
     272                        case 0x6c:
     273                        case 0x6d:
     274                        case 0x6e:
     275                        case 0x6f:
     276                        case 0x70:
     277                        case 0x71:
     278                        case 0x72:
     279                        case 0x73:
     280                        case 0x74:
     281                        case 0x75: /* Next param1 blocks are filled with colour param2 */
     282                                param1 = currOpCode - 0x6b;
     283                                param2 = in->readByte();
     284                                for (l = 0; l < 16; l++) {
     285                                        colours[l] = param2;
     286                                }
     287                                for (k = 0; k < param1; k++) {
     288                                        decodeBlockDelta(offset, colours, 640);
     289                                        offset += TILE_SIZE;
     290                                }
     291                                break;
     292                        case 0x76:
     293                        case 0x77:
     294                        case 0x78:
     295                        case 0x79:
     296                        case 0x7a:
     297                        case 0x7b:
     298                        case 0x7c:
     299                        case 0x7d:
     300                        case 0x7e:
     301                        case 0x7f: /* Next bytes contain colours to fill the next param1 blocks in the current line*/
     302                                param1 = currOpCode - 0x75;
     303                                for (k = 0; k < param1; k++) {
     304                                        param2 = in->readByte();
     305                                        for (l = 0; l < 16; l++) {
     306                                                colours[l] = param2;
     307                                        }
     308                                        decodeBlockDelta(offset, colours, 640);
     309                                        offset += TILE_SIZE;
     310                                }
     311                                break;
     312                        default:
     313                                error("Groovie::VDX: Broken somehow");
     314                }
     315                currOpCode = in->readByte();
     316        }
     317}
     318
     319void VDXPlayer::getStill(Common::ReadStream *in) {
     320        uint16 numXTiles = in->readUint16LE();
     321        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numXTiles=%d", numXTiles);
     322        uint16 numYTiles = in->readUint16LE();
     323        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numYTiles=%d", numYTiles);
     324
     325        // It's skipped in the original:
     326        uint16 colourDepth = in->readUint16LE();
     327        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: colourDepth=%d", colourDepth);
     328
     329        uint16 imageWidth = TILE_SIZE * numXTiles;
     330
     331        uint8 mask = 0;
     332        byte *buf;
     333        if (_flagOne) {
     334                // Paint to the foreground
     335                buf = (byte *)_fg->getBasePtr(0, 0);
     336                if (_flag2Byte) {
     337                        mask = 0xff;
     338                } else {
     339                        mask = 0;
     340                }
     341
     342                // TODO: Verify this is the right procedure. Couldn't find it on the
     343                // disassembly, but it's required to work properly
     344                _flagFirstFrame = true;
     345        } else {
     346                // Paint to the background
     347                buf = (byte *)_bg->getBasePtr(0, 0);
     348        }
     349
     350        // Read the palette
     351        in->read(_palBuf, 3 * 256);
     352
     353        if (_flagSeven) {
     354                _flagFive = true;
     355        }
     356
     357        // Skip the frame when flag 5 is set, unless flag 1 is set
     358        if (!_flagFive || _flagOne) {
     359
     360                byte colours[16];
     361                for (uint16 j = 0; j < numYTiles; j++) {
     362                        for (uint16 i = 0; i < numXTiles; i++) { /* Tile number */
     363                                uint8 colour1 = in->readByte();
     364                                uint8 colour0 = in->readByte();
     365                                uint16 colourMap = in->readUint16LE();
     366                                expandColourMap(colours, colourMap, colour1, colour0);
     367                                decodeBlockStill(buf + j * TILE_SIZE * imageWidth + i * TILE_SIZE, colours, 640, mask);
     368                        }
     369                }
     370
     371                // Apply the palette
     372                if (_flagNine) {
     373                        // Flag 9 starts a fade in
     374                        fadeIn(_palBuf);
     375                } else {
     376                        if (!_flagOne && !_flagSeven) {
     377                                // Actually apply the palette
     378                                setPalette(_palBuf);
     379                        }
     380                }
     381
     382                if (!_flagOne) {
     383                        _vm->_graphicsMan->updateScreen(_bg);
     384                }
     385                /*
     386                if (_flagSix) {
     387                        if (_flagOne) {
     388                                _vm->_graphicsMan->updateScreen(_fg);
     389                        } else {
     390                                _vm->_graphicsMan->updateScreen(_bg);
     391                        }
     392                        _flagSix = 0;
     393                }
     394                */
     395        } else {
     396                // Skip the remaining data
     397                debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Skipping still frame");
     398                while (!in->eos()) {
     399                        in->readByte();
     400                }
     401        }
     402}
     403
     404void VDXPlayer::expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0) {
     405        int flag = 1 << 15;
     406        for (int i = 0; i < 16; i++) {
     407                // Set the corresponding colour
     408                out[i] = (colourMap & flag) ? colour1 : colour0;
     409
     410                // Update the flag to test the next colour
     411                flag >>= 1;
     412        }
     413}
     414
     415void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask) {
     416        for (int y = 0; y < TILE_SIZE; y++) {
     417                for (int x = 0; x < TILE_SIZE; x++) {
     418                        if (_flagOne) {
     419                                // 0xff pixels don't modify the buffer
     420                                if (*colours != 0xff) {
     421                                        // Write the colour
     422                                        *buf = *colours | mask;
     423                                        // Note: if the mask is 0, it paints the image
     424                                        // else, it paints the image's mask using 0xff
     425                                }
     426                        } else {
     427                                *buf = *colours;
     428                        }
     429
     430                        // Point to the next colour
     431                        colours++;
     432
     433                        // Point to the next pixel
     434                        buf++;
     435                }
     436
     437                // Point to the start of the next line
     438                buf += imageWidth - TILE_SIZE;
     439        }
     440}
     441
     442void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth) {
     443        byte *fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset;
     444        //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset;
     445
     446        byte *dest;
     447        // TODO: Verify just the else block is required
     448        //if (_flagOne) {
     449                // Paint to the foreground
     450                //dest = (byte *)_fg->getBasePtr(0, 0) + offset;
     451        //} else {
     452                dest = (byte *)_bg->getBasePtr(0, 0) + offset;
     453        //}
     454
     455        int32 off = _origX + _origY * imageWidth;
     456        for (int y = 0; y < TILE_SIZE; y++) {
     457                for (int x = 0; x < TILE_SIZE; x++) {
     458                        if (_flagSeven) {
     459                                if (fgBuf[off] != 0xff) {
     460                                        if (*colours == 0xff) {
     461                                                dest[off] = fgBuf[off];
     462                                        } else {
     463                                                dest[off] = *colours;
     464                                        }
     465                                }
     466                        } else {
     467                                // Paint directly
     468                                dest[off] = *colours;
     469                        }
     470                        colours++;
     471                        off++;
     472                }
     473
     474                // Prepare the offset of the next line
     475                off += imageWidth - TILE_SIZE;
     476        }
     477}
     478
     479void VDXPlayer::chunkSound(Common::ReadStream *in) {
     480        if (!_audioStream) {
     481                _audioStream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE);
     482                Audio::SoundHandle sound_handle;
     483                ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
     484        }
     485
     486        byte *data = new byte[60000];
     487        int chunksize = in->read(data, 60000);
     488        _audioStream->queueBuffer(data, chunksize);
     489}
     490
     491void VDXPlayer::fadeIn(uint8 *targetpal) {
     492        // Don't do anything if we're asked to skip palette changes
     493        if (_flagSkipPalette)
     494                return;
     495
     496        // TODO: Is it required? If so, move to an appropiate place
     497        // Copy the foreground to the background
     498        memcpy((byte *)_vm->_graphicsMan->_foreground.getBasePtr(0, 0), (byte *)_vm->_graphicsMan->_background.getBasePtr(0, 0), 640 * 320);
     499
     500        // Start a fadein
     501        _vm->_graphicsMan->fadeIn(targetpal);
     502
     503        // Show the background
     504        _vm->_graphicsMan->updateScreen(_bg);
     505}
     506
     507void VDXPlayer::setPalette(uint8 *palette) {
     508        if (_flagSkipPalette)
     509                return;
     510
     511        uint8 palBuf[4 * 256];
     512        debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Setting palette");
     513        for (int i = 0; i < 256; i++) {
     514                palBuf[(i * 4) + 0] = palette[(i * 3) + 0];
     515                palBuf[(i * 4) + 1] = palette[(i * 3) + 1];
     516                palBuf[(i * 4) + 2] = palette[(i * 3) + 2];
     517                palBuf[(i * 4) + 3] = 0;
     518        }
     519        _syst->setPalette(palBuf, 0, 256);
     520}
     521
     522} // End of Groovie namespace
  • engines/groovie/player.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_PLAYER_H
     27#define GROOVIE_PLAYER_H
     28
     29#include "common/system.h"
     30#include "sound/audiostream.h"
     31
     32namespace Groovie {
     33
     34class GroovieEngine;
     35
     36class VideoPlayer {
     37public:
     38        VideoPlayer(GroovieEngine *vm);
     39        virtual ~VideoPlayer() {}
     40
     41        bool load(Common::SeekableReadStream *file, uint16 flags);
     42        bool playFrame();
     43        virtual void setOrigin(int16 x, int16 y) {};
     44
     45protected:
     46        // To be implemented by subclasses
     47        virtual uint16 loadInternal() = 0;
     48        virtual bool playFrameInternal() = 0;
     49
     50        GroovieEngine *_vm;
     51        OSystem *_syst;
     52        Common::SeekableReadStream *_file;
     53        uint16 _flags;
     54        Audio::AppendableAudioStream *_audioStream;
     55
     56private:
     57        // Synchronization stuff
     58        bool _begunPlaying;
     59        uint16 _millisBetweenFrames;
     60        uint32 _lastFrameTime;
     61
     62protected:
     63        void waitFrame();
     64};
     65
     66} // End of Groovie namespace
     67
     68#endif // GROOVIE_PLAYER_H
  • engines/groovie/script.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/debug.h"
     27#include "groovie/music.h"
     28#include "groovie/script.h"
     29#include "groovie/groovie.h"
     30
     31#include "common/config-manager.h"
     32#include "common/endian.h"
     33#include "common/events.h"
     34#include "common/savefile.h"
     35#include "sound/audiocd.h"
     36
     37#define NUM_OPCODES 90
     38
     39namespace Groovie {
     40
     41void debugScript(int level, bool nl, const char *s, ...) {
     42        char buf[STRINGBUFLEN];
     43        va_list va;
     44
     45        uint32 engine_level = kGroovieDebugScript | kGroovieDebugAll;
     46        if (gDebugLevel != 11)
     47                if (!(Common::getEnabledSpecialDebugLevels() & engine_level))
     48                        return;
     49
     50        va_start(va, s);
     51        vsnprintf(buf, STRINGBUFLEN, s, va);
     52        va_end(va);
     53
     54        if (nl)
     55                debug(level, buf);
     56        else
     57                debugN(level, buf);
     58}
     59
     60Script::Script(GroovieEngine *vm) :
     61        _code(NULL), _savedCode(NULL), _stacktop(0),
     62        _debugger(NULL), _error(false), _vm(vm),
     63        _videoFile(NULL), _videoRef(0), _font(NULL) {
     64        // Initialize the random source
     65        _vm->_system->getEventManager()->registerRandomSource(_random, "GroovieScripts");
     66
     67        // Prepare the variables
     68        _bitflags = 0;
     69        for (int i = 0; i < 0x400; i++) {
     70                _variables[i] = 0;
     71        }
     72
     73        // Initialize the music type variable
     74        int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
     75        if (midiDriver == MD_ADLIB) {
     76                // MIDI through AdLib
     77                _variables[0x100] = 0;
     78        } else  if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")) {
     79                // MT-32
     80                _variables[0x100] = 2;
     81        } else {
     82                // GM
     83                _variables[0x100] = 1;
     84        }
     85
     86        _hotspotTopAction = 0;
     87        _hotspotBottomAction = 0;
     88        _hotspotRightAction = 0;
     89        _hotspotLeftAction = 0;
     90        _hotspotCursorOldX = 1000;
     91        _hotspotCursorOldY = 1000;
     92}
     93
     94Script::~Script() {
     95        delete[] _code;
     96        delete[] _savedCode;
     97
     98        delete _font;
     99        delete _videoFile;
     100}
     101
     102void Script::setDebugger(Debugger *debugger) {
     103        _debugger = debugger;
     104}
     105
     106bool Script::loadScript(Common::String filename) {
     107        // Try to open the script file
     108        Common::File scriptfile;
     109        if (!scriptfile.open(filename)) {
     110                return false;
     111        }
     112
     113        // Save the script filename
     114        _scriptFile = filename;
     115
     116        // Load the code
     117        _code = new byte[0x10000];
     118        scriptfile.read(_code, 0x10000);
     119        scriptfile.close();
     120
     121        // Initialize the script
     122        _currentInstruction = 0;
     123
     124        return true;
     125}
     126
     127void Script::directGameLoad(int slot) {
     128        // Reject invalid slots
     129        if (slot < 0 || slot > 9) {
     130                return;
     131        }
     132
     133        // TODO: Return to the main script, likely reusing most of o_returnscript()
     134
     135        // HACK: We set variable 0x19 to the slot to load, and set the current
     136        // instruction to the one that actually loads the saved game specified
     137        // in that variable. This will change in other versions of the game and
     138        // in other games.
     139        _variables[0x19] = slot;
     140        _currentInstruction = 0x287;
     141
     142        // TODO: We'll probably need to start by running the beginning of the
     143        // script to let it do the soundcard initialization and then do the
     144        // actual loading.
     145}
     146
     147void Script::step() {
     148        // Reset the error status
     149        _error = false;
     150
     151        // Prepare the base debug string
     152        char debugstring[10];
     153        sprintf(debugstring, "@0x%04X: ", _currentInstruction);
     154        _debugString = _scriptFile + debugstring;
     155
     156        // Get the current opcode
     157        byte opcode = readScript8bits();
     158        _firstbit = ((opcode & 0x80) != 0);
     159        opcode = opcode & 0x7F;
     160
     161        // Show the opcode debug string
     162        sprintf(debugstring, "op 0x%02X: ", opcode);
     163        _debugString += debugstring;
     164        debugScript(1, false, _debugString.c_str());
     165
     166        // Detect invalid opcodes
     167        if (opcode >= NUM_OPCODES) {
     168                o_invalid();
     169                return;
     170        }
     171
     172        // Execute the current opcode
     173        OpcodeFunc op = _opcodes[opcode];
     174        (this->*op)();
     175}
     176
     177void Script::setMouseClick() {
     178        _eventMouseClicked = true;
     179}
     180
     181void Script::setKbdChar(uint8 c) {
     182        _eventKbdChar = c;
     183}
     184
     185bool Script::haveError() {
     186        return _error;
     187}
     188
     189void Script::error(const char *msg) {
     190        // Prepend the debugging info to the error
     191        Common::String msg2 = _debugString + msg;
     192
     193        // Print the error message
     194        printf("ERROR: %s\n", msg2.c_str());
     195
     196        // Show it in the debugger
     197        _debugger->attach(msg2.c_str());
     198
     199        // Set the error state
     200        _error = true;
     201}
     202
     203uint8 Script::readScript8bits() {
     204        uint8 data = _code[_currentInstruction];
     205        _currentInstruction++;
     206        return data;
     207}
     208
     209uint8 Script::readScriptVar() {
     210        uint8 data = _variables[readScript8or16bits()];
     211        return data;
     212}
     213
     214uint16 Script::readScript16bits() {
     215        uint16 data = READ_LE_UINT16(_code + _currentInstruction);
     216        _currentInstruction += 2;
     217        return data;
     218}
     219
     220uint32 Script::readScript32bits() {
     221        uint32 data = READ_LE_UINT32(_code + _currentInstruction);
     222        _currentInstruction += 4;
     223        return data;
     224}
     225
     226uint16 Script::readScript8or16bits() {
     227        if (_firstbit) {
     228                return readScript8bits();
     229        } else {
     230                return readScript16bits();
     231        }
     232}
     233
     234uint8 Script::readScriptChar(bool allow7C, bool limitVal, bool limitVar) {
     235        uint8 result;
     236        uint8 data = readScript8bits();
     237        if (limitVal) {
     238                data &= 0x7F;
     239        }
     240
     241        if (allow7C && (data == 0x7C)) {
     242                // Index a bidimensional array
     243                uint8 parta, partb;
     244                parta = readScriptChar(false, false, false);
     245                partb = readScriptChar(false, true, true);
     246                result = _variables[0x0A * parta + partb + 0x19];
     247        } else if (data == 0x23) {
     248                // Index an array
     249                data = readScript8bits();
     250                if (limitVar) {
     251                        data &= 0x7F;
     252                }
     253                result = _variables[data - 0x61];
     254        } else {
     255                // Immediate value
     256                result = data - 0x30;
     257        }
     258        return result;
     259}
     260
     261uint16 Script::getVideoRefString() {
     262        Common::String str;
     263        byte c;
     264        while ((c = readScript8bits())) {
     265                switch (c) {
     266                case 0x23:
     267                        c = readScript8bits();
     268                        c = _variables[c - 0x61] + 0x30;
     269                        if (c >= 0x41 && c <= 0x5A) {
     270                                c += 0x20;
     271                        }
     272                        break;
     273                case 0x7C:
     274                        uint8 parta, partb;
     275                        parta = readScriptChar(false, false, false);
     276                        partb = readScriptChar(false, false, false);
     277                        c = _variables[0x0A * parta + partb + 0x19] + 0x30;
     278                        break;
     279                default:
     280                        if (c >= 0x41 && c <= 0x5A) {
     281                                c += 0x20;
     282                        }
     283                }
     284                // Append the current character at the end of the string
     285                str += c;
     286        }
     287
     288        // Add a trailing dot
     289        str += 0x2E;
     290
     291        debugScript(0, false, "%s", str.c_str());
     292
     293        // Extract the script name.
     294        Common::String scriptname(_scriptFile.c_str(), _scriptFile.size() - 4);
     295
     296        // Get the fileref of the resource
     297        return _vm->_resMan->getRef(str, scriptname);
     298}
     299
     300bool Script::hotspot(Common::Rect rect, uint16 address, uint8 cursor) {
     301        // Test if the current mouse position is contained in the specified rectangle
     302        Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
     303        bool contained = rect.contains(mousepos);
     304
     305        // Show hotspots when debugging
     306        if (Common::getEnabledSpecialDebugLevels() & (kGroovieDebugHotspots | kGroovieDebugAll)) {
     307                rect.translate(0, -80);
     308                _vm->_graphicsMan->_foreground.frameRect(rect, 250);
     309                _vm->_system->copyRectToScreen((byte*)_vm->_graphicsMan->_foreground.getBasePtr(0, 0), 640, 0, 80, 640, 320);
     310                _vm->_system->updateScreen();
     311        }
     312
     313        // If there's an already planned action, do nothing
     314        if (_inputAction != -1) {
     315                return false;
     316        }
     317
     318        if (contained) {
     319                // Change the mouse cursor
     320                if (_newCursorStyle == 5) {
     321                        _newCursorStyle = cursor;
     322                }
     323
     324                // If clicked with the mouse, jump to the specified address
     325                if (_mouseClicked) {
     326                        _inputAction = address;
     327                }
     328        }
     329
     330        return contained;
     331}
     332
     333void Script::loadgame(uint slot) {
     334        Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
     335        Common::InSaveFile *file = _vm->_system->getSavefileManager()->openForLoading(filename.c_str());
     336
     337        // Loading the variables. It is endian safe because they're byte variables
     338        file->read(_variables, 0x400);
     339
     340        delete file;
     341}
     342
     343void Script::savegame(uint slot) {
     344        Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
     345        Common::OutSaveFile *file = _vm->_system->getSavefileManager()->openForSaving(filename.c_str());
     346
     347        // Saving the variables. It is endian safe because they're byte variables
     348        file->write(_variables, 0x400);
     349
     350        delete file;
     351}
     352
     353// OPCODES
     354
     355void Script::o_invalid() {
     356        error("Invalid opcode");
     357}
     358
     359void Script::o_nop() {
     360        debugScript(1, true, "NOP");
     361}
     362
     363void Script::o_nop8() {
     364        uint8 tmp = readScript8bits();
     365        debugScript(1, true, "NOP8: 0x%02X", tmp);
     366}
     367
     368void Script::o_nop16() {
     369        uint16 tmp = readScript16bits();
     370        debugScript(1, true, "NOP16: 0x%04X", tmp);
     371}
     372
     373void Script::o_nop32() {
     374        uint32 tmp = readScript32bits();
     375        debugScript(1, true, "NOP32: 0x%08X", tmp);
     376}
     377
     378void Script::o_nop8or16() {
     379        uint16 tmp = readScript8or16bits();
     380        debugScript(1, true, "NOP8OR16: 0x%04X", tmp);
     381}
     382
     383void Script::o_playsong() {                     // 0x02
     384        uint16 fileref = readScript16bits();
     385        debugScript(1, true, "PlaySong(0x%04X): Play xmidi file", fileref);
     386        if (fileref == 0x4C17) {
     387                warning("this song is special somehow");
     388                // don't save the reference?
     389        }
     390        _vm->_musicPlayer->playSong(fileref);
     391}
     392
     393void Script::o_bf9on() {                        // 0x03
     394        debugScript(1, true, "BF9ON: bitflag 9 turned on");
     395        _bitflags |= 1 << 9;
     396}
     397
     398void Script::o_palfadeout() {
     399        debugScript(1, true, "PALFADEOUT");
     400        _vm->_graphicsMan->fadeOut();
     401}
     402
     403void Script::o_bf8on() {                        // 0x05
     404        debugScript(1, true, "BF8ON: bitflag 8 turned on");
     405        _bitflags |= 1 << 8;
     406}
     407
     408void Script::o_bf6on() {                        // 0x06
     409        debugScript(1, true, "BF6ON: bitflag 6 turned on");
     410        _bitflags |= 1 << 6;
     411}
     412
     413void Script::o_bf7on() {                        // 0x07
     414        debugScript(1, true, "BF7ON: bitflag 7 turned on");
     415        _bitflags |= 1 << 7;
     416}
     417
     418void Script::o_setbackgroundsong() {                    // 0x08
     419        uint16 fileref = readScript16bits();
     420        debugScript(1, true, "SetBackgroundSong(0x%04X)", fileref);
     421        _vm->_musicPlayer->setBackgroundSong(fileref);
     422}
     423
     424void Script::o_videofromref() {                 // 0x09
     425        uint16 fileref = readScript16bits();
     426
     427        // Show the debug information just when starting the playback
     428        if (fileref != _videoRef) {
     429                debugScript(1, false, "VIDEOFROMREF(0x%04X) (Not fully imp): Play video file from ref", fileref);
     430                debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Playing video 0x%04X via 0x09", fileref);
     431        }
     432        switch (fileref) {
     433        case 0x1C03:    // Trilobyte logo
     434        case 0x1C04:    // Virgin logo
     435        case 0x1C05:    // Credits
     436                if (fileref != _videoRef) {
     437                        debugScript(1, true, "Use external file if available");
     438                }
     439                break;
     440               
     441        case 0x400D:    // floating objects in music room
     442        case 0x5060:    // a sound from gamwav?
     443        case 0x5098:    // a sound from gamwav?
     444        case 0x2402:    // House becomes book in intro?
     445        case 0x1426:    // Turn to face front in hall: played after intro
     446        case 0x206D:    // Cards on table puzzle (bedroom)
     447        case 0x2001:    // Coins on table puzzle (bedroom)
     448                if (fileref != _videoRef) {
     449                        debugScript(1, false, " (This video is special somehow!)");
     450                        warning("(This video is special somehow!)");
     451                        printf("Special vid: 0x%04X\n", fileref);
     452                }
     453        }
     454        if (fileref != _videoRef) {
     455                debugScript(1, true, "");
     456        }
     457        // Play the video
     458        if (!playvideofromref(fileref)) {
     459                // Move _currentInstruction back
     460                _currentInstruction -= 3;
     461        }
     462}
     463
     464bool Script::playvideofromref(uint16 fileref) {
     465        // It isn't the current video, open it
     466        if (fileref != _videoRef) {
     467
     468                // Debug bitflags
     469                debugScript(1, false, "Play video 0x%04X (bitflags:", fileref);
     470                for (int i = 10; i >= 0; i--) {
     471                        debugScript(1, false, "%d", _bitflags & (1 << i)? 1 : 0);
     472                }
     473                debugScript(1, true, ")");
     474
     475                // Close the previous video file
     476                if (_videoFile) {
     477                        _videoRef = 0;
     478                        delete _videoFile;
     479                }
     480
     481                // Try to open the new file
     482                _videoFile = _vm->_resMan->open(fileref);
     483
     484                if (_videoFile) {
     485                        _videoRef = fileref;
     486                        _vm->_videoPlayer->load(_videoFile, _bitflags);
     487                } else {
     488                        error("Couldn't open file");
     489                        return true;
     490                }
     491
     492                _bitflags = 0;
     493        }
     494
     495        // Video available, play one frame
     496        if (_videoFile) {
     497                bool endVideo = _vm->_videoPlayer->playFrame();
     498
     499                if (endVideo) {
     500                        // Close the file
     501                        delete _videoFile;
     502                        _videoFile = NULL;
     503                        _videoRef = 0;
     504
     505                        // Clear the input events while playing the video
     506                        _eventMouseClicked = false;
     507                        _eventKbdChar = 0;
     508
     509                        // Newline
     510                        debugScript(1, true, "");
     511                }
     512
     513                // Let the caller know if the video has ended
     514                return endVideo;
     515        }
     516
     517        // If the file is closed, finish the playback
     518        return true;
     519}
     520
     521void Script::o_bf5on() {                        // 0x0A
     522        debugScript(1, true, "BF5ON: bitflag 5 turned on");
     523        _bitflags |= 1 << 5;
     524}
     525
     526void Script::o_inputloopstart() {
     527        debugScript(5, true, "Input loop start");
     528
     529        // Reset the input action and the mouse cursor
     530        _inputAction = -1;
     531        _newCursorStyle = 5;
     532
     533        // Save the input loop address
     534        _inputLoopAddress = _currentInstruction - 1;
     535
     536        // Save the current mouse state for the whole loop
     537        _mouseClicked = _eventMouseClicked;
     538        _eventMouseClicked = false;
     539
     540        // Save the current pressed character for the whole loop
     541        _kbdChar = _eventKbdChar;
     542        _eventKbdChar = 0;
     543}
     544
     545void Script::o_keyboardaction() {
     546        uint8 val = readScript8bits();
     547        uint16 address = readScript16bits();
     548        debugScript(5, true, "Test key == 0x%02X @0x%04X", val, address);
     549
     550        // If there's an already planned action, do nothing
     551        if (_inputAction != -1) {
     552                return;
     553        }
     554
     555        // Check the typed key
     556        if (_kbdChar == val) {
     557                // Exit the input loop
     558                _inputLoopAddress = 0;
     559
     560                // Save the action address
     561                _inputAction = address;
     562        }
     563}
     564
     565void Script::o_hotspot_rect() {
     566        uint16 left = readScript16bits();
     567        uint16 top = readScript16bits();
     568        uint16 right = readScript16bits();
     569        uint16 bottom = readScript16bits();
     570        uint16 address = readScript16bits();
     571        uint8 cursor = readScript8bits();
     572        debugScript(5, true, "HOTSPOT-RECT(%d,%d,%d,%d) @0x%04X cursor=%d", left, top, right, bottom, address, cursor);
     573
     574        // Mark the specified rectangle
     575        Common::Rect rect(left, top, right, bottom);
     576        hotspot(rect, address, cursor);
     577}
     578
     579void Script::o_hotspot_left() {
     580        uint16 address = readScript16bits();
     581        debugScript(5, true, "HOTSPOT-LEFT @0x%04X", address);
     582
     583        // Mark the leftmost 100 pixels of the game area
     584        Common::Rect rect(0, 80, 100, 400);
     585        hotspot(rect, address, 1);
     586}
     587
     588void Script::o_hotspot_right() {
     589        uint16 address = readScript16bits();
     590        debugScript(5, true, "HOTSPOT-RIGHT @0x%04X", address);
     591
     592        // Mark the rightmost 100 pixels of the game area
     593        Common::Rect rect(540, 80, 640, 400);
     594        hotspot(rect, address, 2);
     595}
     596
     597void Script::o_hotspot_center() {
     598        uint16 address = readScript16bits();
     599        debugScript(5, true, "HOTSPOT-CENTER @0x%04X", address);
     600
     601        // Mark the centremost 240 pixels of the game area
     602        Common::Rect rect(200, 80, 440, 400);
     603        hotspot(rect, address, 0);
     604}
     605
     606void Script::o_hotspot_current() {
     607       uint16 address = readScript16bits();
     608       debugScript(5, true, "HOTSPOT-CURRENT @0x%04X", address);
     609
     610       // The original interpreter doesn't check the position, so accept the
     611       // whole screen
     612       Common::Rect rect(0, 0, 640, 480);
     613       hotspot(rect, address, 0);
     614}
     615
     616void Script::o_inputloopend() {
     617        debugScript(5, true, "Input loop end");
     618
     619        // Handle the predefined hotspots
     620        if (_hotspotTopAction) {
     621                Common::Rect rect(0, 0, 640, 80);
     622                hotspot(rect, _hotspotTopAction, _hotspotTopCursor);
     623        }
     624        if (_hotspotBottomAction) {
     625                Common::Rect rect(0, 400, 640, 480);
     626                hotspot(rect, _hotspotBottomAction, _hotspotBottomCursor);
     627        }
     628        if (_hotspotRightAction) {
     629                Common::Rect rect(560, 0, 640, 480);
     630                hotspot(rect, _hotspotRightAction, 2);
     631        }
     632        if (_hotspotLeftAction) {
     633                Common::Rect rect(0, 0, 80, 480);
     634                hotspot(rect, _hotspotLeftAction, 1);
     635        }
     636
     637        // Actually execute the planned action
     638        if (_inputAction != -1) {
     639                // Jump to the planned address
     640                _currentInstruction = _inputAction;
     641
     642                // Exit the input loop
     643                _inputLoopAddress = 0;
     644                _vm->_system->showMouse(false);
     645        }
     646
     647        // Nothing to do
     648        if (_inputLoopAddress) {
     649                if (_newCursorStyle != _vm->_cursorMan->getStyle()) {
     650                        _vm->_cursorMan->setStyle(_newCursorStyle);
     651                }
     652                _vm->_system->showMouse(true);
     653
     654                // Go back to the begining of the loop
     655                _currentInstruction = _inputLoopAddress;
     656
     657                // There's nothing to do until we get some input
     658                _vm->waitForInput();
     659        }
     660}
     661
     662void Script::o_random() {
     663        uint16 varnum = readScript8or16bits();
     664        uint8 maxnum = readScript8bits();
     665        debugScript(1, true, "RANDOM: var[0x%04X] = rand(%d)", varnum, maxnum);
     666
     667        _variables[varnum] = _random.getRandomNumber(maxnum);
     668}
     669
     670void Script::o_jmp() {
     671        uint16 address = readScript16bits();
     672        debugScript(1, true, "JMP @0x%04X", address);
     673
     674        // Set the current address
     675        _currentInstruction = address;
     676}
     677
     678void Script::o_loadstring() {
     679        uint16 varnum = readScript8or16bits();
     680        debugScript(1, false, "LOADSTRING var[0x%04X..] =", varnum);
     681        do {
     682                _variables[varnum++] = readScriptChar(true, true, true);
     683                debugScript(1, false, " 0x%02X", _variables[varnum - 1]);
     684        } while (!(_code[_currentInstruction - 1] & 0x80));
     685        debugScript(1, true, "");
     686}
     687
     688void Script::o_ret() {
     689        uint8 val = readScript8bits();
     690        debugScript(1, true, "RET %d", val);
     691
     692        // Set the return value
     693        _variables[0x102] = val;
     694
     695        // Get the return address
     696        if (_stacktop > 0) {
     697                _stacktop--;
     698                _currentInstruction = _stack[_stacktop];
     699        } else {
     700                error("Return: Stack is empty");
     701        }
     702}
     703
     704void Script::o_call() {
     705        uint16 address = readScript16bits();
     706        debugScript(1, true, "CALL @0x%04X", address);
     707
     708        // Save return address in the call stack
     709        _stack[_stacktop] = _currentInstruction;
     710        _stacktop++;
     711
     712        // Change the current instruction
     713        _currentInstruction = address;
     714}
     715
     716void Script::o_sleep() {
     717        uint16 time = readScript16bits();
     718        debugScript(1, true, "SLEEP 0x%04X", time);
     719
     720        _vm->_system->delayMillis(time * 3);
     721}
     722
     723void Script::o_strcmpnejmp() {                  // 0x1A
     724        uint16 varnum = readScript8or16bits();
     725        debugScript(1, false, "STRCMP-NEJMP: var[0x%04X..],", varnum);
     726        uint8 val;
     727        uint8 result = 1;
     728        do {
     729                val = readScriptChar(true, true, true);
     730
     731                if (_variables[varnum] != val) {
     732                        result = 0;
     733                }
     734                varnum++;
     735                debugScript(1, false, " 0x%02X", val);
     736
     737        } while (!(_code[_currentInstruction - 1] & 0x80));
     738
     739        uint16 address = readScript16bits();
     740        if (!result) {
     741                debugScript(1, true, " jumping to @0x%04X", address);
     742                _currentInstruction = address;
     743        } else {
     744                debugScript(1, true, " not jumping");
     745        }
     746}
     747
     748void Script::o_xor_obfuscate() {
     749        uint16 varnum = readScript8or16bits();
     750        debugScript(1, false, "XOR OBFUSCATE: var[0x%04X..] = ", varnum);
     751        do {
     752                uint8 val = readScript8bits();
     753                _firstbit = ((val & 0x80) != 0);
     754                val &= 0x4F;
     755
     756                _variables[varnum] ^= val;
     757                debugScript(1, false, "%c", _variables[varnum]);
     758
     759                varnum++;
     760        } while (!_firstbit);
     761        debugScript(1, true, "");
     762}
     763
     764void Script::o_vdxtransition() {                // 0x1C
     765        uint16 fileref = readScript16bits();
     766
     767        // Show the debug information just when starting the playback
     768        if (fileref != _videoRef) {
     769                debugScript(1, true, "VDX transition fileref = 0x%04X", fileref);
     770                debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Playing video 0x%04X with transition", fileref);
     771        }
     772
     773        // Set bit 1
     774        _bitflags |= 1 << 1;
     775
     776        // Clear bit 7
     777        _bitflags &= ~(1 << 7);
     778
     779        // Set bit 2 if _firstbit
     780        if (_firstbit) {
     781                _bitflags |= 1 << 2;
     782        }
     783
     784        // Play the video
     785        if (!playvideofromref(fileref)) {
     786                // Move _currentInstruction back
     787                _currentInstruction -= 3;
     788        }
     789}
     790
     791void Script::o_swap() {
     792        uint16 varnum1 = readScript8or16bits();
     793        uint16 varnum2 = readScript16bits();
     794        debugScript(1, true, "SWAP var[0x%04X] <-> var[0x%04X]", varnum1, varnum2);
     795
     796        uint8 tmp = _variables[varnum1];
     797        _variables[varnum1] = _variables[varnum2];
     798        _variables[varnum2] = tmp;
     799}
     800
     801void Script::o_inc() {
     802        uint16 varnum = readScript8or16bits();
     803        debugScript(1, true, "INC var[0x%04X]", varnum);
     804
     805        _variables[varnum]++;
     806}
     807
     808void Script::o_dec() {
     809        uint16 varnum = readScript8or16bits();
     810        debugScript(1, true, "DEC var[0x%04X]", varnum);
     811
     812        _variables[varnum]--;
     813}
     814
     815void Script::o_strcmpnejmp_var() {                      // 0x21
     816        uint16 data = readScriptVar();
     817        if (data > 9) {
     818                data -= 7;
     819        }
     820        data = _variables[data + 0x19];
     821        bool stringsmatch = 1;
     822        do {
     823                if (_variables[data++] != readScriptChar(true, true, true)) {
     824                        stringsmatch = 0;
     825                }       
     826        } while (!(_code[_currentInstruction - 1] & 0x80));
     827
     828        uint16 offset = readScript16bits();
     829        if (!stringsmatch) {
     830                _currentInstruction = offset;
     831        }
     832}
     833
     834void Script::o_copybgtofg() {                   // 0x22
     835        debugScript(1, true, "COPY_BG_TO_FG");
     836        memcpy(_vm->_graphicsMan->_foreground.getBasePtr(0, 0), _vm->_graphicsMan->_background.getBasePtr(0, 0), 640 * 320);
     837}
     838
     839void Script::o_strcmpeqjmp() {                  // 0x23
     840        uint16 varnum = readScript8or16bits();
     841        debugScript(1, false, "STRCMP-EQJMP: var[0x%04X..],", varnum);
     842        uint8 val;
     843        uint8 result = 1;
     844        do {
     845                val = readScriptChar(true, true, true);
     846
     847                if (_variables[varnum] != val) {
     848                        result = 0;
     849                }
     850                varnum++;
     851                debugScript(1, false, " 0x%02X", val);
     852
     853        } while (!(_code[_currentInstruction - 1] & 0x80));
     854
     855        uint16 address = readScript16bits();
     856        if (result) {
     857                debugScript(1, true, " jumping to @0x%04X", address);
     858                _currentInstruction = address;
     859        } else {
     860                debugScript(1, true, " not jumping");
     861        }
     862}
     863
     864void Script::o_mov() {
     865        uint16 varnum1 = readScript8or16bits();
     866        uint16 varnum2 = readScript16bits();
     867        debugScript(1, true, "MOV var[0x%04X] = var[0x%04X]", varnum1, varnum2);
     868
     869        _variables[varnum1] = _variables[varnum2];
     870}
     871
     872void Script::o_add() {
     873        uint16 varnum1 = readScript8or16bits();
     874        uint16 varnum2 = readScript16bits();
     875        debugScript(1, true, "ADD var[0x%04X] += var[0x%04X]", varnum1, varnum2);
     876
     877        _variables[varnum1] += _variables[varnum2];
     878}
     879
     880void Script::o_videofromstring1() {
     881        uint16 instStart = _currentInstruction;
     882        uint16 fileref = getVideoRefString();
     883
     884        // Show the debug information just when starting the playback
     885        if (fileref != _videoRef) {
     886                debugScript(0, true, "VIDEOFROMSTRING1 0x%04X", fileref);
     887        }
     888
     889        // Play the video
     890        if (!playvideofromref(fileref)) {
     891                // Move _currentInstruction back
     892                _currentInstruction = instStart - 1;
     893        }
     894}
     895
     896void Script::o_videofromstring2() {
     897        uint16 instStart = _currentInstruction;
     898        uint16 fileref = getVideoRefString();
     899
     900        // Show the debug information just when starting the playback
     901        if (fileref != _videoRef) {
     902                debugScript(0, true, "VIDEOFROMSTRING2 0x%04X", fileref);
     903        }
     904
     905        // Set bit 1
     906        _bitflags |= 1 << 1;
     907
     908        // Set bit 2 if _firstbit
     909        if (_firstbit) {
     910                _bitflags |= 1 << 2;
     911        }
     912
     913        // Play the video
     914        if (!playvideofromref(fileref)) {
     915                // Move _currentInstruction back
     916                _currentInstruction = instStart - 1;
     917        }
     918}
     919
     920void Script::o_stopmidi() {
     921        debugScript(1, true, "STOPMIDI (TODO)");
     922}
     923
     924void Script::o_endscript() {
     925        debugScript(1, true, "END OF SCRIPT");
     926        _error = true;
     927}
     928
     929void Script::o_sethotspottop() {
     930        uint16 address = readScript16bits();
     931        uint8 cursor = readScript8bits();
     932        debugScript(5, true, "SETHOTSPOTTOP @0x%04X cursor=%d", address, cursor);
     933
     934        _hotspotTopAction = address;
     935        _hotspotTopCursor = cursor;
     936}
     937
     938void Script::o_sethotspotbottom() {
     939        uint16 address = readScript16bits();
     940        uint8 cursor = readScript8bits();
     941        debugScript(5, true, "SETHOTSPOTBOTTOM @0x%04X cursor=%d", address, cursor);
     942
     943        _hotspotBottomAction = address;
     944        _hotspotBottomCursor = cursor;
     945}
     946
     947void Script::o_loadgame() {
     948        uint16 varnum = readScript8or16bits();
     949        uint8 slot = _variables[varnum];
     950        debugScript(1, true, "LOADGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
     951
     952        loadgame(slot);
     953        _vm->_system->clearScreen();
     954}
     955
     956void Script::o_savegame() {
     957        uint16 varnum = readScript8or16bits();
     958        uint8 slot = _variables[varnum];
     959        debugScript(1, true, "SAVEGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
     960
     961        savegame(slot);
     962}
     963
     964void Script::o_hotspotbottom_4() {      //0x30
     965        uint16 address = readScript16bits();
     966        debugScript(5, true, "HOTSPOT-BOTTOM @0x%04X", address);
     967
     968        // Mark the 80 pixels under the game area
     969        Common::Rect rect(0, 400, 640, 480);
     970        hotspot(rect, address, 4);
     971}
     972
     973void Script::o_midivolume() {
     974        uint16 arg1 = readScript16bits();
     975        uint16 arg2 = readScript16bits();
     976        debugScript(1, true, "MIDI volume: %d %d", arg1, arg2);
     977        _vm->_musicPlayer->setGameVolume(arg1, arg2);
     978}
     979
     980void Script::o_jne() {
     981        int16 varnum1 = readScript8or16bits();
     982        uint16 varnum2 = readScript16bits();
     983        uint16 address = readScript16bits();
     984        debugScript(1, false, "JNE: var[var[0x%04X] - 0x31] != var[0x%04X] @0x%04X", varnum1, varnum2, address);
     985
     986        if (_variables[_variables[varnum1] - 0x31] != _variables[varnum2]) {
     987                _currentInstruction = address;
     988                debugScript(1, true, " jumping to @0x%04X", address);
     989        } else {
     990                debugScript(1, true, " not jumping");
     991        }
     992}
     993
     994void Script::o_loadstringvar() {
     995        uint16 varnum = readScript8or16bits();
     996        varnum = _variables[varnum] - 0x31;
     997        debugScript(1, false, "LOADSTRINGVAR var[0x%04X..] =", varnum);
     998        do {
     999                _variables[varnum++] = readScriptChar(true, true, true);
     1000                debugScript(1, false, " 0x%02X", _variables[varnum - 1]);
     1001        } while (!(_code[_currentInstruction - 1] & 0x80));
     1002        debugScript(1, true, "");
     1003}
     1004
     1005void Script::o_chargreatjmp() {
     1006        uint16 varnum = readScript8or16bits();
     1007        debugScript(1, false, "CHARGREAT-JMP: var[0x%04X..],", varnum);
     1008        uint8 val;
     1009        uint8 result = 0;
     1010        do {
     1011                val = readScriptChar(true, true, true);
     1012
     1013                if (val < _variables[varnum]) {
     1014                        result = 1;
     1015                }
     1016                varnum++;
     1017                debugScript(1, false, " 0x%02X", val);
     1018        } while (!(_code[_currentInstruction - 1] & 0x80));
     1019
     1020        uint16 address = readScript16bits();
     1021        if (result) {
     1022                debugScript(1, true, " jumping to @0x%04X", address);
     1023                _currentInstruction = address;
     1024        } else {
     1025                debugScript(1, true, " not jumping");
     1026        }
     1027}
     1028
     1029void Script::o_bf7off() {
     1030        debugScript(1, true, "BF7OFF: bitflag 7 turned off");
     1031        _bitflags &= ~(1 << 7);
     1032}
     1033
     1034void Script::o_charlessjmp() {
     1035        uint16 varnum = readScript8or16bits();
     1036        debugScript(1, false, "CHARLESS-JMP: var[0x%04X..],", varnum);
     1037        uint8 val;
     1038        uint8 result = 0;
     1039        do {
     1040                val = readScriptChar(true, true, true);
     1041
     1042                if (val > _variables[varnum]) {
     1043                        result = 1;
     1044                }
     1045                varnum++;
     1046                debugScript(1, false, " 0x%02X", val);
     1047        } while (!(_code[_currentInstruction - 1] & 0x80));
     1048
     1049        uint16 address = readScript16bits();
     1050        if (result) {
     1051                debugScript(1, true, " jumping to @0x%04X", address);
     1052                _currentInstruction = address;
     1053        } else {
     1054                debugScript(1, true, " not jumping");
     1055        }
     1056}
     1057
     1058void Script::o_copyrecttobg() { // 0x37
     1059        uint16 left = readScript16bits();
     1060        uint16 top = readScript16bits();
     1061        uint16 right = readScript16bits();
     1062        uint16 bottom = readScript16bits();
     1063        debugScript(1, true, "COPYRECT((%d,%d)->(%d,%d))", left, top, right, bottom);
     1064
     1065        uint16 i, width = right - left, height = bottom - top;
     1066        uint32 offset = 0;
     1067        byte *fg, *bg;
     1068        fg = (byte *)_vm->_graphicsMan->_foreground.getBasePtr(left, top - 80);
     1069        bg = (byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - 80);
     1070        for (i = 0; i < height; i++) {
     1071                memcpy(bg + offset, fg + offset, width);
     1072                offset += 640;
     1073        }
     1074        _vm->_system->copyRectToScreen((byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - 80), 640, left, top, width, height);
     1075        _vm->_graphicsMan->change();
     1076}
     1077
     1078void Script::o_restorestkpnt() {
     1079        debugScript(1, true, "Restore stack pointer from saved (TODO)");
     1080}
     1081
     1082void Script::o_obscureswap() {
     1083        debugScript(1, true, "OBSCSWAP");
     1084        uint16 var1, var2, tmp;
     1085
     1086        // Read the first variable
     1087        var1 = readScriptChar(false, true, true) * 10;
     1088        var1 += readScriptChar(false, true, true) + 0x19;
     1089
     1090        // Read the second variable
     1091        var2 = readScriptChar(false, true, true) * 10;
     1092        var2 += readScriptChar(false, true, true) + 0x19;
     1093
     1094        // Swap the values
     1095        tmp = _variables[var1];
     1096        _variables[var1] = _variables[var2];
     1097        _variables[var2] = tmp;
     1098}
     1099
     1100void Script::o_printstring() {
     1101        char stringstorage[15], newchar;
     1102        uint8 counter = 0;
     1103        memset(stringstorage, 0, 15);
     1104        do {
     1105                newchar = readScriptChar(true, true, true) + 0x30;
     1106                if (newchar < 0x30 || newchar > 0x39) {         // If character is invalid, chuck a space in
     1107                        if (newchar < 0x41 || newchar > 0x7A) {
     1108                                newchar = 0x20;
     1109                        }
     1110                }
     1111
     1112                stringstorage[counter] = newchar;
     1113                counter++;
     1114        } while (!(_code[_currentInstruction - 1] & 0x80));
     1115
     1116        stringstorage[counter] = 0;
     1117
     1118        // Load the font if required
     1119        if (!_font) {
     1120                _font = new Font(_vm->_system);
     1121        }
     1122        _font->printstring(stringstorage);
     1123}
     1124
     1125void Script::o_hotspot_slot() {
     1126        uint16 slot = readScript8bits();
     1127        uint16 left = readScript16bits();
     1128        uint16 top = readScript16bits();
     1129        uint16 right = readScript16bits();
     1130        uint16 bottom = readScript16bits();
     1131        uint16 address = readScript16bits();
     1132        uint16 cursor = readScript8bits();
     1133        debugScript(1, true, "HOTSPOT-SLOT %d (%d,%d,%d,%d) @0x%04X cursor=%d (TODO)", slot, left, top, right, bottom, address, cursor);
     1134
     1135        Common::Rect rect(left, top, right, bottom);
     1136        if (hotspot(rect, address, cursor)) {
     1137                char savename[15];
     1138
     1139                Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
     1140                Common::StringList files = _vm->_system->getSavefileManager()->listSavefiles(filename.c_str());
     1141                if (!files.empty()) {
     1142                        Common::InSaveFile *file = _vm->_system->getSavefileManager()->openForLoading(filename.c_str());
     1143                        if (file) {
     1144                                uint8 i;
     1145                                char temp;
     1146
     1147                                for (i = 0; i < 15; i++) {
     1148                                        file->read(&temp, 1);
     1149                                        savename[i] = temp + 0x30;
     1150                                }
     1151
     1152                                delete file;
     1153                        } else {
     1154                                strcpy(savename, "ERROR");
     1155                        }
     1156                } else {
     1157                        strcpy(savename, "E M P T Y");
     1158                }
     1159
     1160                // Load the font if required
     1161                if (!_font) {
     1162                        _font = new Font(_vm->_system);
     1163                }
     1164                _font->printstring(savename);
     1165        } else {
     1166                Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
     1167                if (_hotspotCursorOldX != mousepos.x || _hotspotCursorOldY != mousepos.y ) {
     1168                        Common::Rect topbar(640, 80);
     1169
     1170                        Graphics::Surface *gamescreen;
     1171                        gamescreen = _vm->_system->lockScreen();
     1172
     1173                        gamescreen->fillRect(topbar, 0);       
     1174
     1175                        _vm->_system->unlockScreen();
     1176                        _hotspotCursorOldX = mousepos.x;
     1177                        _hotspotCursorOldY = mousepos.y;
     1178                }
     1179        }
     1180}
     1181
     1182void Script::o_checkvalidsaves() {
     1183        debugScript(1, true, "CHECKVALIDSAVES");
     1184
     1185        // Reset the array of valid saves
     1186        for (int i = 0; i < 10; i++) {
     1187                _variables[i] = 0;
     1188        }
     1189
     1190        // Get the list of savefiles
     1191        Common::String pattern = ConfMan.getActiveDomainName() + ".00?";
     1192        Common::StringList savefiles = _vm->_system->getSavefileManager()->listSavefiles(pattern.c_str());
     1193
     1194        // Mark the existing savefiles as valid
     1195        uint count = 0;
     1196        Common::StringList::iterator it = savefiles.begin();
     1197        while (it != savefiles.end()) {
     1198                int8 n = it->lastChar() - '0';
     1199                if (n >= 0 && n <= 9) {
     1200                        // TODO: Check the contents of the file?
     1201                        debugScript(2, true, "  Found valid savegame: %s", it->c_str());
     1202                        _variables[n] = 1;
     1203                        count++;
     1204                }
     1205                it++;
     1206        }
     1207
     1208        // Save the number of valid saves
     1209        _variables[0x104] = count;
     1210        debugScript(1, true, "  Found %d valid savegames", count);
     1211}
     1212
     1213void Script::o_resetvars() {
     1214        debugScript(1, true, "RESETVARS");
     1215        for (int i = 0; i < 0x100; i++) {
     1216                _variables[i] = 0;
     1217        }
     1218}
     1219
     1220void Script::o_mod() {
     1221        uint16 varnum = readScript8or16bits();
     1222        uint8 val = readScript8bits();
     1223        debugScript(1, true, "MOD var[0x%04X] %%= %d", varnum, val);
     1224
     1225        _variables[varnum] %= val;
     1226}
     1227
     1228void Script::o_loadscript() {
     1229        Common::String filename;
     1230        char c;
     1231        while ((c = readScript8bits())) {
     1232                filename += c;
     1233        }
     1234        debugScript(1, true, "LOADSCRIPT %s", filename.c_str());
     1235
     1236        // Just 1 level of sub-scripts are allowed
     1237        if (_savedCode) {
     1238                error("Tried to load a level 2 sub-script");
     1239        }
     1240
     1241        // Save the current code
     1242        _savedCode = _code;
     1243        _savedInstruction = _currentInstruction;
     1244
     1245        // Save the filename of the current script
     1246        _savedScriptFile = _scriptFile;
     1247
     1248        // Load the sub-script
     1249        if (!loadScript(filename)) {
     1250                error("Couldn't load sub-script");
     1251        }
     1252
     1253        // Save the current stack top
     1254        _savedStacktop = _stacktop;
     1255
     1256        // Save the variables
     1257        memcpy(_savedVariables, _variables + 0x107, 0x180);
     1258}
     1259
     1260void Script::o_setvideoorigin() {
     1261        // Set bitflag 7
     1262        _bitflags |= 1 << 7;
     1263
     1264        // Read the two offset arguments
     1265        int16 origX = readScript16bits();
     1266        int16 origY = readScript16bits();
     1267
     1268        debugScript(1, true, "SetVideoOrigin(0x%04X,0x%04X) (%d, %d)", origX, origY, origX, origY);
     1269        _vm->_videoPlayer->setOrigin(origX, origY);
     1270}
     1271
     1272void Script::o_sub() {
     1273        uint16 varnum1 = readScript8or16bits();
     1274        uint16 varnum2 = readScript16bits();
     1275        debugScript(1, true, "SUB var[0x%04X] -= var[0x%04X]", varnum1, varnum2);
     1276
     1277        _variables[varnum1] -= _variables[varnum2];
     1278}
     1279
     1280void Script::o_othello() {
     1281        uint16 arg = readScript8bits();
     1282        debugScript(1, true, "OTHELLO var[0x%02X]", arg);
     1283
     1284        // Arguments used by the original implementation: (2, arg, scriptBoard)
     1285        byte *scriptBoard = &_variables[0x19];
     1286        byte board[7][7];
     1287        for (int y = 0; y < 7; y++) {
     1288                for (int x = 0; x < 7; x++) {
     1289                        board[x][y] = 0;
     1290                        if (*scriptBoard == 0x32) board[x][y] = 1;
     1291                        if (*scriptBoard == 0x42) board[x][y] = 2;
     1292                        scriptBoard++;
     1293                        printf("%d", board[x][y]);
     1294                }
     1295                printf("\n");
     1296        }
     1297
     1298        // Set the movement origin
     1299        _variables[0] = 6; // y
     1300        _variables[1] = 0; // x
     1301        // Set the movement destination
     1302        _variables[2] = 6;
     1303        _variables[3] = 1;
     1304}
     1305
     1306void Script::o_returnscript() {
     1307        uint8 val = readScript8bits();
     1308        debugScript(1, true, "RETURNSCRIPT @0x%02X", val);
     1309
     1310        // Are we returning from a sub-script?
     1311        if (!_savedCode) {
     1312                error("Tried to return from the main script");
     1313        }
     1314
     1315        // Set the return value
     1316        _variables[0x102] = val;
     1317
     1318        // Restore the code
     1319        delete[] _code;
     1320        _code = _savedCode;
     1321        _savedCode = NULL;
     1322        _currentInstruction = _savedInstruction;
     1323
     1324        // Restore the stack
     1325        _stacktop = _savedStacktop;
     1326
     1327        // Restore the variables
     1328        memcpy(_variables + 0x107, _savedVariables, 0x180);
     1329
     1330        // Restore the filename of the script
     1331        _scriptFile = _savedScriptFile;
     1332
     1333        //TODO: reset script flags and previous video's flag1?
     1334        _vm->_videoPlayer->setOrigin(0, 0);
     1335}
     1336
     1337void Script::o_sethotspotright() {
     1338        uint16 address = readScript16bits();
     1339        debugScript(1, true, "SETHOTSPOTRIGHT @0x%04X", address);
     1340
     1341        _hotspotRightAction = address;
     1342}
     1343
     1344void Script::o_sethotspotleft() {
     1345        uint16 address = readScript16bits();
     1346        debugScript(1, true, "SETHOTSPOTLEFT @0x%04X", address);
     1347
     1348        _hotspotLeftAction = address;
     1349}
     1350
     1351void Script::o_getcd() {
     1352        debugScript(1, true, "GETCD");
     1353
     1354        // By default set it to no CD available
     1355        int8 cd = -1;
     1356
     1357        // Try to open one file from each CD
     1358        Common::File cdfile;
     1359        if (cdfile.open("b.gjd")) {
     1360                cdfile.close();
     1361                cd = 1;
     1362        }
     1363        if (cdfile.open("at.gjd")) {
     1364                cdfile.close();
     1365                if (cd == 1) {
     1366                        // Both CDs are available
     1367                        cd = 0;
     1368                } else {
     1369                        cd = 2;
     1370                }
     1371        }
     1372
     1373        _variables[0x106] = cd;
     1374}
     1375
     1376void Script::o_opcode4D() {
     1377        // TODO: play alternative vie logo, then playcd
     1378        uint8 val = readScript8bits();
     1379        debugScript(1, true, "PLAYCD? %d", val);
     1380
     1381        if (val == 2) {
     1382                AudioCD.play(1, 1, 0, 0);
     1383        }
     1384
     1385}
     1386
     1387void Script::o_hotspot_outrect() {
     1388        uint16 left = readScript16bits();
     1389        uint16 top = readScript16bits();
     1390        uint16 right = readScript16bits();
     1391        uint16 bottom = readScript16bits();
     1392        uint16 address = readScript16bits();
     1393        debugScript(1, true, "HOTSPOT-OUTRECT(%d,%d,%d,%d) @0x%04X (TODO)", left, top, right, bottom, address);
     1394
     1395        // Test if the current mouse position is outside the specified rectangle
     1396        Common::Rect rect(left, top, right, bottom);
     1397        Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
     1398        bool contained = rect.contains(mousepos);
     1399
     1400        if (!contained) {
     1401                error("hotspot-outrect unimplemented!");
     1402                // TODO: what to do with address?
     1403        }
     1404}
     1405
     1406void Script::o_stub56() {
     1407        uint32 val1 = readScript32bits();
     1408        uint8 val2 = readScript8bits();
     1409        uint8 val3 = readScript8bits();
     1410        debugScript(1, true, "STUB56: 0x%08X 0x%02X 0x%02X", val1, val2, val3);
     1411}
     1412
     1413void Script::o_stub59() {
     1414        uint16 val1 = readScript8or16bits();
     1415        uint8 val2 = readScript8bits();
     1416        debugScript(1, true, "STUB59: 0x%04X 0x%02X", val1, val2);
     1417}
     1418
     1419Script::OpcodeFunc Script::_opcodes[NUM_OPCODES] = {
     1420        &Script::o_nop, // 0x00
     1421        &Script::o_nop,
     1422        &Script::o_playsong,
     1423        &Script::o_bf9on,
     1424        &Script::o_palfadeout, // 0x04
     1425        &Script::o_bf8on,
     1426        &Script::o_bf6on,
     1427        &Script::o_bf7on,
     1428        &Script::o_setbackgroundsong, // 0x08
     1429        &Script::o_videofromref,
     1430        &Script::o_bf5on,
     1431        &Script::o_inputloopstart,
     1432        &Script::o_keyboardaction, // 0x0C
     1433        &Script::o_hotspot_rect,
     1434        &Script::o_hotspot_left,
     1435        &Script::o_hotspot_right,
     1436        &Script::o_hotspot_center, // 0x10
     1437        &Script::o_hotspot_center,
     1438        &Script::o_hotspot_current,
     1439        &Script::o_inputloopend,
     1440        &Script::o_random, // 0x14
     1441        &Script::o_jmp,
     1442        &Script::o_loadstring,
     1443        &Script::o_ret,
     1444        &Script::o_call, // 0x18
     1445        &Script::o_sleep,
     1446        &Script::o_strcmpnejmp,
     1447        &Script::o_xor_obfuscate,
     1448        &Script::o_vdxtransition, // 0x1C
     1449        &Script::o_swap,
     1450        &Script::o_nop8,
     1451        &Script::o_inc,
     1452        &Script::o_dec, // 0x20
     1453        &Script::o_strcmpnejmp_var,
     1454        &Script::o_copybgtofg,
     1455        &Script::o_strcmpeqjmp,
     1456        &Script::o_mov, // 0x24
     1457        &Script::o_add,
     1458        &Script::o_videofromstring1, // Reads a string and then does stuff: used by book in library
     1459        &Script::o_videofromstring2, // play vdx file from string, after setting 1 (and 2 if firstbit)
     1460        &Script::o_nop16, // 0x28
     1461        &Script::o_stopmidi,
     1462        &Script::o_endscript,
     1463        &Script::o_nop,
     1464        &Script::o_sethotspottop, // 0x2C
     1465        &Script::o_sethotspotbottom,
     1466        &Script::o_loadgame,
     1467        &Script::o_savegame,
     1468        &Script::o_hotspotbottom_4, // 0x30
     1469        &Script::o_midivolume,
     1470        &Script::o_jne,
     1471        &Script::o_loadstringvar,
     1472        &Script::o_chargreatjmp, // 0x34
     1473        &Script::o_bf7off,
     1474        &Script::o_charlessjmp,
     1475        &Script::o_copyrecttobg,
     1476        &Script::o_restorestkpnt, // 0x38
     1477        &Script::o_obscureswap,
     1478        &Script::o_printstring,
     1479        &Script::o_hotspot_slot,
     1480        &Script::o_checkvalidsaves, // 0x3C
     1481        &Script::o_resetvars,
     1482        &Script::o_mod,
     1483        &Script::o_loadscript,
     1484        &Script::o_setvideoorigin, // 0x40
     1485        &Script::o_sub,
     1486        &Script::o_othello,
     1487        &Script::o_returnscript,
     1488        &Script::o_sethotspotright, // 0x44
     1489        &Script::o_sethotspotleft,
     1490        &Script::o_nop,
     1491        &Script::o_nop,
     1492        &Script::o_nop8, // 0x48
     1493        &Script::o_nop,
     1494        &Script::o_nop16,
     1495        &Script::o_nop8,
     1496        &Script::o_getcd, // 0x4C
     1497        &Script::o_opcode4D,
     1498        &Script::o_nop16,
     1499        &Script::o_nop16,
     1500        &Script::o_nop16, // 0x50
     1501        &Script::o_nop16,
     1502        //&Script::o_nop8,
     1503        &Script::o_invalid,             // Do loads with game area, maybe draw dirty areas?
     1504        &Script::o_hotspot_outrect,
     1505        &Script::o_nop, // 0x54
     1506        &Script::o_nop16,
     1507        &Script::o_stub56,
     1508        //&Script::o_nop32,
     1509        &Script::o_invalid,             // completely unimplemented, plays vdx in some way
     1510        //&Script::o_nop, // 0x58
     1511        &Script::o_invalid, // 0x58     // like above, but plays from string not ref
     1512        &Script::o_stub59
     1513};
     1514
     1515} // End of Groovie namespace
  • engines/groovie/graphics.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_GRAPHICS_H
     27#define GROOVIE_GRAPHICS_H
     28
     29namespace Groovie {
     30
     31class GroovieEngine;
     32
     33class GraphicsMan {
     34public:
     35        GraphicsMan(GroovieEngine *vm);
     36        ~GraphicsMan();
     37
     38        // Buffers
     39        void update();
     40        void change();
     41        void mergeFgAndBg();
     42        void updateScreen(Graphics::Surface *source);
     43        Graphics::Surface _foreground;  // The main surface that most things are drawn to
     44        Graphics::Surface _background;  // Used occasionally, mostly (only?) in puzzles
     45
     46        // Palette fading
     47        bool isFading();
     48        void fadeIn(byte *pal);
     49        void fadeOut();
     50
     51private:
     52        GroovieEngine *_vm;
     53
     54        bool _changed;
     55
     56        // Palette fading
     57        void applyFading(int step);
     58        int _fading;
     59        byte _paletteFull[256 * 4];
     60        uint32 _fadeStartTime;
     61};
     62
     63} // End of Groovie namespace
     64
     65#endif // GROOVIE_GRAPHICS_H
  • engines/groovie/roq.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_ROQ_H
     27#define GROOVIE_ROQ_H
     28
     29#include "groovie/player.h"
     30
     31namespace Groovie {
     32
     33class GroovieEngine;
     34
     35struct ROQBlockHeader {
     36        uint16 type;
     37        uint32 size;
     38        uint16 param;
     39};
     40
     41class ROQPlayer : public VideoPlayer {
     42public:
     43        ROQPlayer(GroovieEngine *vm);
     44        ~ROQPlayer();
     45
     46protected:
     47        uint16 loadInternal();
     48        bool playFrameInternal();
     49
     50private:
     51        bool readBlockHeader(ROQBlockHeader &blockHeader);
     52
     53        bool processBlock();
     54        bool processBlockInfo(ROQBlockHeader &blockHeader);
     55        bool processBlockQuadCodebook(ROQBlockHeader &blockHeader);
     56        bool processBlockQuadVector(ROQBlockHeader &blockHeader);
     57        bool processBlockQuadVectorSub(ROQBlockHeader &blockHeader);
     58        bool processBlockStill(ROQBlockHeader &blockHeader);
     59        bool processBlockSoundMono(ROQBlockHeader &blockHeader);
     60        bool processBlockSoundStereo(ROQBlockHeader &blockHeader);
     61        bool processBlockAudioContainer(ROQBlockHeader &blockHeader);
     62
     63        uint16 _num2blocks;
     64        uint16 _num4blocks;
     65};
     66
     67} // End of Groovie namespace
     68
     69#endif // GROOVIE_ROQ_H
  • engines/groovie/font.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "common/file.h"
     27#include "graphics/surface.h"
     28
     29#include "groovie/font.h"
     30
     31namespace Groovie {
     32
     33Font::Font(OSystem *syst) :
     34        _syst(syst), _sphinxfnt(NULL) {
     35
     36        Common::File fontfile;
     37        if (!fontfile.open("sphinx.fnt")) {
     38                error("Groovie::Font: Couldn't open sphinx.fnt");
     39        }
     40        uint16 fontfilesize = fontfile.size();
     41        _sphinxfnt = fontfile.readStream(fontfilesize);
     42        fontfile.close();
     43}
     44
     45Font::~Font() {
     46        delete _sphinxfnt;
     47}
     48
     49void Font::printstring(char *messagein) {
     50        uint16 totalwidth = 0, currxoffset, i;
     51
     52        char message[15];
     53        memset(message, 0, 15);
     54
     55        // Clear the top bar
     56        Common::Rect topbar(640, 80);
     57        Graphics::Surface *gamescreen;
     58        gamescreen = _syst->lockScreen();
     59        gamescreen->fillRect(topbar, 0);       
     60        _syst->unlockScreen();
     61
     62        for (i = 0; i < 14; i++) {
     63                char chartocopy = messagein[i];
     64                if (chartocopy <= 0x00 || chartocopy == 0x24) {
     65                        break;
     66                }
     67                message[i] = chartocopy;
     68        }
     69        Common::rtrim(message);
     70        for (i = 0; i < strlen(message); i++) {
     71                totalwidth += letterwidth(message[i]);
     72        }
     73        currxoffset = (640 - totalwidth) / 2;
     74        char *currpos = message;
     75        while (*(currpos) != 0) {
     76                currxoffset += printletter(*(currpos++), currxoffset);
     77        }
     78}
     79
     80uint16 Font::letteroffset(char letter) {
     81        uint16 offset;
     82        offset = letter;
     83        _sphinxfnt->seek(offset);
     84        offset = _sphinxfnt->readByte() * 2 + 128;
     85        _sphinxfnt->seek(offset);
     86        offset = _sphinxfnt->readUint16LE();
     87        return offset;
     88}
     89
     90uint8 Font::letterwidth(char letter) {
     91        uint16 offset = letteroffset(letter);
     92        _sphinxfnt->seek(offset);
     93        return _sphinxfnt->readByte();
     94}
     95
     96uint8 Font::letterheight(char letter) {
     97        uint16 offset, width, julia, data, counter = 0;
     98        offset = letteroffset(letter);
     99        _sphinxfnt->seek(offset);
     100        width = _sphinxfnt->readByte();
     101        julia = _sphinxfnt->readByte();
     102        data = _sphinxfnt->readByte();
     103        while (data != 0xFF) {
     104                data = _sphinxfnt->readByte();
     105                counter++;
     106        }
     107        if (counter % width != 0) assert("font file corrupt");
     108        return counter / width;
     109}
     110
     111
     112uint8 Font::printletter(char letter, uint16 xoffset) {
     113        uint16 offset, width, height, julia;
     114        //printf("%c (%d), ", letter, letter);
     115        offset = letteroffset(letter);
     116        height = letterheight(letter);
     117        _sphinxfnt->seek(offset);
     118        width = _sphinxfnt->readByte();
     119        julia = _sphinxfnt->readByte();
     120
     121        byte *data = new byte[width * height];
     122        _sphinxfnt->read(data, width * height);
     123        _syst->copyRectToScreen(data, width, xoffset, 16, width, height);
     124        delete data;
     125       
     126        return width;
     127}
     128
     129} // End of Groovie namespace
  • engines/groovie/vdx.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_VDX_H
     27#define GROOVIE_VDX_H
     28
     29#include "groovie/player.h"
     30
     31namespace Groovie {
     32
     33class VDXPlayer : public VideoPlayer {
     34public:
     35        VDXPlayer(GroovieEngine *vm);
     36        ~VDXPlayer();
     37        void setOrigin(int16 x, int16 y);
     38
     39protected:
     40        uint16 loadInternal();
     41        bool playFrameInternal();
     42
     43private:
     44        Graphics::Surface *_fg, *_bg;
     45        uint8 _palBuf[3 * 256];
     46
     47        // Origin
     48        int16 _origX, _origY;
     49
     50        // Video flags
     51        bool _flagZero;
     52        bool _flagOne;
     53        bool _flagOnePrev;
     54        byte _flag2Byte;
     55        bool _flagThree;
     56        bool _flagFour;
     57        bool _flagFive;
     58        bool _flagSix;
     59        bool _flagSeven;
     60        bool _flagEight;
     61        bool _flagNine;
     62
     63        bool _flagSkipStill;
     64        bool _flagSkipPalette;
     65        bool _flagFirstFrame;
     66        bool _flagTransparent;
     67        bool _flagUpdateStill;
     68
     69        void getStill(Common::ReadStream *in);
     70        void getDelta(Common::ReadStream *in);
     71        void expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0);
     72        void decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask);
     73        void decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth);
     74        void chunkSound(Common::ReadStream *in);
     75        void setPalette(uint8 *palette);
     76        void fadeIn(uint8 *palette);
     77};
     78
     79} // End of Groovie namespace
     80
     81#endif // GROOVIE_VDX_H
  • engines/groovie/script.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_SCRIPT_H
     27#define GROOVIE_SCRIPT_H
     28
     29#include "common/file.h"
     30#include "common/rect.h"
     31
     32#include "groovie/font.h"
     33
     34namespace Groovie {
     35
     36class GroovieEngine;
     37
     38class Script {
     39        friend class Debugger;
     40
     41public:
     42        Script(GroovieEngine *vm);
     43        ~Script();
     44
     45        void setDebugger(Debugger *debugger);
     46
     47        bool loadScript(Common::String scriptfile);
     48        void directGameLoad(int slot);
     49        void step();
     50
     51        void setMouseClick();
     52        void setKbdChar(uint8 c);
     53
     54        bool haveError();
     55
     56private:
     57        GroovieEngine *_vm;
     58
     59        Common::RandomSource _random;
     60
     61        bool _firstbit;
     62
     63        // Script filename (for debugging purposes)
     64        Common::String _scriptFile;
     65        Common::String _savedScriptFile;
     66
     67        // Code
     68        byte *_code;
     69        uint16 _currentInstruction;
     70        byte *_savedCode;
     71        uint16 _savedInstruction;
     72
     73        // Variables
     74        byte _variables[0x400];
     75        byte _savedVariables[0x180];
     76
     77        // Stack
     78        uint16 _stack[0x20];
     79        uint8 _stacktop;
     80        uint8 _savedStacktop;
     81
     82        // Input
     83        bool _mouseClicked;
     84        bool _eventMouseClicked;
     85        uint8 _kbdChar;
     86        uint8 _eventKbdChar;
     87        uint16 _inputLoopAddress;
     88        int16 _inputAction;
     89        uint8 _newCursorStyle;
     90        uint16 _hotspotTopAction;
     91        uint16 _hotspotTopCursor;
     92        uint16 _hotspotBottomAction;
     93        uint16 _hotspotBottomCursor;
     94        uint16 _hotspotRightAction;
     95        uint16 _hotspotLeftAction;
     96        uint16 _hotspotCursorOldX;
     97        uint16 _hotspotCursorOldY;
     98
     99        // Video
     100        Font *_font;
     101        Common::SeekableReadStream *_videoFile;
     102        uint16 _videoRef;
     103        uint16 _bitflags;
     104
     105        // Debugging
     106        Debugger *_debugger;
     107        Common::String _debugString;
     108        void error(const char *msg);
     109        bool _error;
     110
     111        // Helper functions
     112        uint8 readScript8bits();
     113        uint16 readScript16bits();
     114        uint32 readScript32bits();
     115        uint16 readScript8or16bits();
     116        uint8 readScriptChar(bool allow7C, bool limitVal, bool limitVar);
     117        uint8 readScriptVar();
     118        uint16 getVideoRefString();
     119
     120        bool hotspot(Common::Rect rect, uint16 addr, uint8 cursor);
     121
     122        void loadgame(uint slot);
     123        void savegame(uint slot);
     124        bool playvideofromref(uint16 fileref);
     125
     126        // Opcodes
     127        typedef void (Script::*OpcodeFunc)();
     128        static OpcodeFunc _opcodes[];
     129
     130        void o_invalid();
     131
     132        void o_nop();
     133        void o_nop8();
     134        void o_nop16();
     135        void o_nop32();
     136        void o_nop8or16();
     137
     138        void o_playsong();
     139        void o_bf9on();
     140        void o_palfadeout();
     141        void o_bf8on();
     142        void o_bf6on();
     143        void o_bf7on();
     144        void o_setbackgroundsong();
     145        void o_videofromref();
     146        void o_bf5on();
     147        void o_inputloopstart();
     148        void o_keyboardaction();
     149        void o_hotspot_rect();
     150        void o_hotspot_left();
     151        void o_hotspot_right();
     152        void o_hotspot_center();
     153        void o_hotspot_current();
     154        void o_inputloopend();
     155        void o_random();
     156        void o_jmp();
     157        void o_loadstring();
     158        void o_ret();
     159        void o_call();
     160        void o_sleep();
     161        void o_strcmpnejmp_var();
     162        void o_copybgtofg();
     163        void o_strcmpnejmp();
     164        void o_xor_obfuscate();
     165        void o_vdxtransition();
     166        void o_swap();
     167        void o_inc();
     168        void o_dec();
     169        void o_strcmpeqjmp();
     170        void o_mov();
     171        void o_add();
     172        void o_videofromstring1();
     173        void o_videofromstring2();
     174        void o_stopmidi();
     175        void o_endscript();
     176        void o_sethotspottop();
     177        void o_sethotspotbottom();
     178        void o_loadgame();
     179        void o_savegame();
     180        void o_hotspotbottom_4();
     181        void o_midivolume();
     182        void o_jne();
     183        void o_loadstringvar();
     184        void o_chargreatjmp();
     185        void o_bf7off();
     186        void o_charlessjmp();
     187        void o_copyrecttobg();
     188        void o_restorestkpnt();
     189        void o_obscureswap();
     190        void o_printstring();
     191        void o_hotspot_slot();
     192        void o_checkvalidsaves();
     193        void o_resetvars();
     194        void o_mod();
     195        void o_loadscript();
     196        void o_setvideoorigin();
     197        void o_sub();
     198        void o_othello();
     199        void o_returnscript();
     200        void o_sethotspotright();
     201        void o_sethotspotleft();
     202        void o_getcd();
     203        void o_opcode4D();
     204        void o_hotspot_outrect();
     205        void o_stub56();
     206        void o_stub59();
     207};
     208
     209} // End of Groovie namespace
     210
     211#endif // GROOVIE_SCRIPT_H
  • engines/groovie/groovie.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "common/config-manager.h"
     27#include "common/events.h"
     28#include "sound/mixer.h"
     29
     30#include "groovie/groovie.h"
     31#include "groovie/music.h"
     32#include "groovie/roq.h"
     33#include "groovie/vdx.h"
     34
     35namespace Groovie {
     36
     37GroovieEngine::GroovieEngine(OSystem *syst, GroovieGameDescription *gd) :
     38        Engine(syst), _gameDescription(gd), _debugger(NULL), _script(this),
     39        _resMan(NULL), _cursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
     40        _graphicsMan(NULL), _waitingForInput(false) {
     41
     42        // Adding the default directories
     43        Common::File::addDefaultDirectory(_gameDataDir.getChild("groovie"));
     44        Common::File::addDefaultDirectory(_gameDataDir.getChild("media"));
     45        Common::File::addDefaultDirectory(_gameDataDir.getChild("system"));
     46
     47        // Initialize the custom debug levels
     48        Common::addSpecialDebugLevel(kGroovieDebugAll, "All", "Debug everything");
     49        Common::addSpecialDebugLevel(kGroovieDebugVideo, "Video", "Debug video and audio playback");
     50        Common::addSpecialDebugLevel(kGroovieDebugResource, "Resource", "Debug resouce management");
     51        Common::addSpecialDebugLevel(kGroovieDebugScript, "Script", "Debug the scripts");
     52        Common::addSpecialDebugLevel(kGroovieDebugUnknown, "Unknown", "Report values of unknown data in files");
     53        Common::addSpecialDebugLevel(kGroovieDebugHotspots, "Hotspots", "Show the hotspots");
     54        Common::addSpecialDebugLevel(kGroovieDebugCursor, "Cursor", "Debug cursor decompression / switching");
     55}
     56
     57GroovieEngine::~GroovieEngine() {
     58        // Delete the remaining objects
     59        delete _debugger;
     60        delete _resMan;
     61        delete _cursorMan;
     62        delete _videoPlayer;
     63        delete _musicPlayer;
     64        delete _graphicsMan;
     65}
     66
     67Common::Error GroovieEngine::init() {
     68        // Initialize the graphics
     69        _system->beginGFXTransaction();
     70        initCommonGFX(true);
     71        _system->initSize(640, 480);
     72        _system->endGFXTransaction();
     73
     74        // Create debugger. It requires GFX to be initialized
     75        _debugger = new Debugger(this);
     76        _script.setDebugger(_debugger);
     77
     78        // Create the graphics manager
     79        _graphicsMan = new GraphicsMan(this);
     80
     81        // Create the resource and cursor managers and the video player
     82        switch (_gameDescription->version) {
     83        case kGroovieT7G:
     84                _resMan = new ResMan_t7g();
     85                _cursorMan = new CursorMan_t7g(_system);
     86                _videoPlayer = new VDXPlayer(this);
     87                break;
     88        case kGroovieV2:
     89                _resMan = new ResMan_v2();
     90                _cursorMan = new CursorMan_v2(_system);
     91                _videoPlayer = new ROQPlayer(this);
     92                break;
     93        }
     94
     95        // Create the music player
     96        _musicPlayer = new MusicPlayer(this);
     97
     98        // Load volume levels
     99        syncSoundSettings();
     100
     101        // Get the name of the main script
     102        Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName;
     103        if (_gameDescription->version == kGroovieT7G) {
     104                // Run The 7th Guest's demo if requested
     105                if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode")) {
     106                        filename = Common::String("demo.grv");
     107                }
     108        } else if (_gameDescription->version == kGroovieV2) {
     109                // Open the disk index
     110                Common::File disk;
     111                if (!disk.open(filename)) {
     112                        error("Couldn't open %s", filename.c_str());
     113                        return Common::kNoGameDataFoundError;
     114                }
     115
     116                // Search the entry
     117                bool found = false;
     118                int index = 0;
     119                while (!found && !disk.eos()) {
     120                        Common::String line = disk.readLine();
     121                        if (line.hasPrefix("title: ")) {
     122                                // A new entry
     123                                index++;
     124                        } else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) {
     125                                // It's the boot of the entry were looking for,
     126                                // get the script filename
     127                                filename = line.c_str() + 6;
     128                                found = true;
     129                        }
     130                }
     131
     132                // Couldn't find the entry
     133                if (!found) {
     134                        error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str());
     135                        return Common::kUnknownError;
     136                }
     137        }
     138       
     139        // Check the script file extension
     140        if (!filename.hasSuffix(".grv")) {
     141                error("%s isn't a valid script filename", filename.c_str());
     142                return Common::kUnknownError;
     143        }
     144
     145        // Load the script
     146        if (!_script.loadScript(filename)) {
     147                error("Couldn't load the script file %s", filename.c_str());
     148                return Common::kUnknownError;
     149        }
     150
     151        // Should I load a saved game?
     152        if (ConfMan.hasKey("save_slot")) {
     153                // Get the requested slot
     154                int slot = ConfMan.getInt("save_slot");
     155                _script.directGameLoad(slot);
     156        }
     157
     158        return Common::kNoError;
     159}
     160
     161Common::Error GroovieEngine::go() {
     162        // Check that the game files and the audio tracks aren't together run from
     163        // the same cd
     164       
     165        checkCD();
     166
     167        // Initialize the CD
     168        int cd_num = ConfMan.getInt("cdrom");
     169        if (cd_num >= 0)
     170                _system->openCD(cd_num);
     171
     172        while (!shouldQuit()) {
     173                // Show the debugger if required
     174                if (_debugger->isAttached()) {
     175                        _debugger->onFrame();
     176                }
     177
     178                // If there's still a script error after debugging, end the execution
     179                if (_script.haveError()) {
     180                        quitGame();
     181                        break;
     182                }
     183
     184                // Handle input
     185                Common::Event ev;
     186                while (_eventMan->pollEvent(ev)) {
     187                        switch (ev.type) {
     188                        case Common::EVENT_KEYDOWN:
     189                                // CTRL-D: Attach the debugger
     190                                if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
     191                                        _debugger->attach();
     192
     193                                // Send the event to the scripts
     194                                _script.setKbdChar(ev.kbd.ascii);
     195
     196                                // Continue the script execution to handle the key
     197                                _waitingForInput = false;
     198                                break;
     199
     200                        case Common::EVENT_MOUSEMOVE:
     201                                // Continue the script execution, the mouse
     202                                // pointer may fall inside a hotspot now
     203                                _waitingForInput = false;
     204                                break;
     205
     206                        case Common::EVENT_LBUTTONDOWN:
     207                                // Send the event to the scripts
     208                                _script.setMouseClick();
     209
     210                                // Continue the script execution to handle
     211                                // the click
     212                                _waitingForInput = false;
     213                                break;
     214
     215                        case Common::EVENT_QUIT:
     216                                quitGame();
     217                                break;
     218
     219                        default:
     220                                break;
     221                        }
     222                }
     223
     224                if (_waitingForInput) {
     225                        // Still waiting for input, just update the mouse and wait a bit more
     226                        _cursorMan->animate();
     227                        _system->updateScreen();
     228                        _system->delayMillis(50);
     229                } else if (_graphicsMan->isFading()) {
     230                        // We're waiting for a fading to end, let the CPU rest
     231                        // for a while and continue
     232                        _system->delayMillis(30);
     233                } else {
     234                        // Everything's fine, execute another script step
     235                        _script.step();
     236                }
     237
     238                // Update the screen if required
     239                _graphicsMan->update();
     240        }
     241
     242        return Common::kNoError;
     243}
     244
     245bool GroovieEngine::hasFeature(EngineFeature f) const {
     246        return
     247                (f == kSupportsRTL) ||
     248                (f == kSupportsLoadingDuringRuntime);
     249}
     250
     251void GroovieEngine::syncSoundSettings() {
     252        _musicPlayer->setUserVolume(ConfMan.getInt("music_volume"));
     253        _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("speech_volume"));
     254}
     255
     256bool GroovieEngine::canLoadGameStateCurrently() {
     257        // TODO: verify the engine has been initialized
     258        return true;
     259}
     260
     261Common::Error GroovieEngine::loadGameState(int slot) {
     262        _script.directGameLoad(slot);
     263
     264        // TODO: Use specific error codes
     265        return Common::kNoError;
     266}
     267
     268void GroovieEngine::waitForInput() {
     269        _waitingForInput = true;
     270}
     271
     272} // End of namespace Groovie
  • engines/groovie/module.mk

     
     1MODULE := engines/groovie
     2
     3MODULE_OBJS := \
     4        cursor.o \
     5        debug.o \
     6        detection.o \
     7        font.o \
     8        graphics.o \
     9        groovie.o \
     10        jpeg.o \
     11        lzss.o \
     12        music.o \
     13        player.o \
     14        resource.o \
     15        roq.o \
     16        script.o \
     17        vdx.o
     18
     19# This module can be built as a plugin
     20ifeq ($(ENABLE_GROOVIE), DYNAMIC_PLUGIN)
     21PLUGIN := 1
     22endif
     23
     24# Include common rules
     25include $(srcdir)/rules.mk
  • engines/groovie/font.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_FONT_H
     27#define GROOVIE_FONT_H
     28
     29#include "common/stream.h"
     30#include "common/system.h"
     31
     32namespace Groovie {
     33
     34class Font {
     35public:
     36        Font(OSystem *syst);
     37        ~Font();
     38        void printstring(char *messagein);
     39
     40private:
     41        OSystem *_syst;
     42        Common::MemoryReadStream *_sphinxfnt;
     43
     44        uint16 letteroffset(char letter);
     45        uint8 letterwidth(char letter);
     46        uint8 letterheight(char letter);
     47        uint8 printletter(char letter, uint16 xoffset);
     48};
     49
     50} // End of Groovie namespace
     51
     52#endif // GROOVIE_FONT_H
  • engines/groovie/cursor.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/cursor.h"
     27#include "groovie/groovie.h"
     28
     29namespace Groovie {
     30
     31// Cursor Manager
     32
     33CursorMan::CursorMan(OSystem *system) :
     34        _syst(system), _lastTime(0), _cursor(NULL) {
     35}
     36
     37CursorMan::~CursorMan() {
     38        // Delete the cursors
     39        for (uint cursor = 0; cursor < _cursors.size(); cursor++) {
     40                delete _cursors[cursor];
     41        }
     42}
     43
     44uint8 CursorMan::getStyle() {
     45        return _current;
     46}
     47
     48void CursorMan::setStyle(uint8 newStyle) {
     49        // Reset the animation
     50        _lastFrame = 254;
     51        _lastTime = 1;
     52
     53        // Save the new cursor
     54        _current = newStyle;
     55        _cursor = _cursors[newStyle];
     56
     57        // Show the first frame
     58        _cursor->enable();
     59        animate();
     60}
     61
     62void CursorMan::animate() {
     63        if (_lastTime) {
     64                int newTime = _syst->getMillis();
     65                if (_lastTime - newTime >= 75) {
     66                        _lastFrame++;
     67                        _lastFrame %= _cursor->getFrames();
     68                        _cursor->showFrame(_lastFrame);
     69                        _lastTime = _syst->getMillis();
     70                }
     71        }
     72}
     73
     74
     75// t7g Cursor
     76
     77class Cursor_t7g : public Cursor {
     78public:
     79        Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal);
     80
     81        void enable();
     82        void showFrame(uint16 frame);
     83
     84private:
     85        OSystem *_syst;
     86        byte *_img;
     87        byte *_pal;
     88};
     89
     90Cursor_t7g::Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal) :
     91        _syst(system), _pal(pal) {
     92
     93        _width = img[0];
     94        _height = img[1];
     95        _numFrames = img[2];
     96        uint8 elinor1 = img[3];
     97        uint8 elinor2 = img[4];
     98
     99        _img = img + 5;
     100
     101        debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
     102        debugC(1, kGroovieDebugCursor | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::Cursor: elinor: 0x%02X (%d), 0x%02X (%d)", elinor1, elinor1, elinor2, elinor2);
     103}
     104
     105void Cursor_t7g::enable() {
     106        // Apply the palette
     107        _syst->setCursorPalette(_pal, 0, 32);
     108}
     109
     110void Cursor_t7g::showFrame(uint16 frame) {
     111        // Set the mouse cursor
     112        int offset = _width * _height * frame;
     113        _syst->setMouseCursor((const byte *)_img + offset, _width, _height, _width >> 1, _height >> 1, 0);
     114}
     115
     116
     117// t7g Cursor Manager
     118
     119#define NUM_IMGS 9
     120static const uint16 cursorDataOffsets[NUM_IMGS] = {
     1210x0000, 0x182f, 0x3b6d, 0x50cc, 0x6e79, 0x825d, 0x96d7, 0xa455, 0xa776
     122};
     123
     124#define NUM_PALS 7
     125//Pals: 0xb794, 0xb7f4, 0xb854, 0xb8b4, 0xb914, 0xb974, 0xb9d4
     126
     127#define NUM_STYLES 11
     128// pyramid is cursor 8, eyes are 9 & 10
     129const uint CursorMan_t7g::_cursorImg[NUM_STYLES] = {3, 5, 4, 3, 1, 0, 2, 6, 7, 8, 8};
     130const uint CursorMan_t7g::_cursorPal[NUM_STYLES] = {0, 0, 0, 0, 2, 0, 1, 3, 5, 4, 6};
     131
     132CursorMan_t7g::CursorMan_t7g(OSystem *system) :
     133        CursorMan(system) {
     134
     135        // Open the cursors file
     136        Common::File robgjd;
     137        if (!robgjd.open("rob.gjd")) {
     138                error("Groovie::Cursor: Couldn't open rob.gjd");
     139                return;
     140        }
     141
     142        // Load the images
     143        for (uint imgnum = 0; imgnum < NUM_IMGS; imgnum++) {
     144                robgjd.seek(cursorDataOffsets[imgnum]);
     145                _images.push_back(loadImage(robgjd));
     146        }
     147
     148        // Load the palettes
     149        robgjd.seek(-0x60 * NUM_PALS, SEEK_END);
     150        for (uint palnum = 0; palnum < NUM_PALS; palnum++) {
     151                _palettes.push_back(loadPalette(robgjd));
     152        }
     153
     154        // Build the cursors
     155        for (uint cursor = 0; cursor < NUM_STYLES; cursor++) {
     156                Cursor *s = new Cursor_t7g(_syst, _images[_cursorImg[cursor]], _palettes[_cursorPal[cursor]]);
     157                _cursors.push_back(s);
     158        }
     159
     160        robgjd.close();
     161}
     162
     163CursorMan_t7g::~CursorMan_t7g() {
     164        // Delete the images
     165        for (uint img = 0; img < _images.size(); img++) {
     166                delete[] _images[img];
     167        }
     168
     169        // Delete the palettes
     170        for (uint pal = 0; pal < _palettes.size(); pal++) {
     171                delete[] _palettes[pal];
     172        }
     173}
     174
     175byte *CursorMan_t7g::loadImage(Common::File &file) {
     176        uint16 decompbytes = 0, offset, i, length;
     177        uint8 flagbyte, lengthmask = 0x0F, offsetlen, var_8;
     178        byte *cursorStorage = new byte[65536];
     179        uint8 *runningcursor = cursorStorage;
     180
     181        bool finished = false;
     182        while (!(finished || file.eos())) {
     183                flagbyte = file.readByte();
     184                for (i = 1; i <= 8; i++) {
     185                        if (!file.eos()) {
     186                                if (flagbyte & 1) {
     187                                        *(runningcursor++) = file.readByte();
     188                                        decompbytes++;
     189                                } else {
     190                                        var_8 = file.readByte();
     191                                        offsetlen = file.readByte();
     192                                        if (var_8 == 0 && offsetlen == 0) {
     193                                                finished = true;
     194                                                break;
     195                                        }
     196                                        length = (offsetlen & lengthmask) + 3;
     197                                        offsetlen >>= 4;
     198                                        offset = (offsetlen << 8) + var_8;
     199                                        decompbytes += length;
     200
     201                                        for (; length > 0; length--, runningcursor++) {
     202                                                *(runningcursor) = *(runningcursor - offset);
     203                                        }
     204                                }
     205                                flagbyte = flagbyte >> 1;
     206                        }
     207                }
     208        }
     209
     210        return cursorStorage;
     211}
     212
     213byte *CursorMan_t7g::loadPalette(Common::File &file) {
     214        byte *palette = new byte[4 * 32];
     215        for (uint8 colournum = 0; colournum < 32; colournum++) {
     216                palette[colournum * 4 + 0] = file.readByte();
     217                palette[colournum * 4 + 1] = file.readByte();
     218                palette[colournum * 4 + 2] = file.readByte();
     219                palette[colournum * 4 + 3] = 0;
     220        }
     221        return palette;
     222}
     223
     224
     225// v2 Cursor
     226
     227class Cursor_v2 : public Cursor {
     228public:
     229        Cursor_v2(Common::File &file);
     230
     231        void enable();
     232        void showFrame(uint16 frame);
     233
     234private:
     235        //byte *_data;
     236};
     237
     238Cursor_v2::Cursor_v2(Common::File &file) {
     239        _numFrames = file.readUint16LE();
     240        _width = file.readUint16LE();
     241        _height = file.readUint16LE();
     242
     243        debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
     244
     245        uint16 tmp16 = file.readUint16LE();
     246        printf("hotspot x?: %d\n", tmp16);
     247        tmp16 = file.readUint16LE();
     248        printf("hotspot y?: %d\n", tmp16);
     249        int loop2count = file.readUint16LE();
     250        printf("loop2count?: %d\n", loop2count);
     251        for (int l = 0; l < loop2count; l++) {
     252                tmp16 = file.readUint16LE();
     253                printf("loop2a: %d\n", tmp16);
     254                tmp16 = file.readUint16LE();
     255                printf("loop2b: %d\n", tmp16);
     256        }
     257
     258        file.seek(0x20 * 3, SEEK_CUR);
     259
     260        for (int f = 0; f < _numFrames; f++) {
     261                uint32 tmp32 = file.readUint32LE();
     262                printf("loop3: %d\n", tmp32);
     263
     264                //file.seek(tmp32, SEEK_CUR);
     265                byte *data = new byte[tmp32];
     266                file.read(data, tmp32);
     267                //Common::hexdump(data, tmp32);
     268                delete[] data;
     269        }
     270}
     271
     272void Cursor_v2::enable() {
     273}
     274
     275void Cursor_v2::showFrame(uint16 frame) {
     276}
     277
     278
     279// v2 Cursor Manager
     280
     281CursorMan_v2::CursorMan_v2(OSystem *system) :
     282        CursorMan(system) {
     283
     284        // Open the icons file
     285        Common::File iconsFile;
     286        if (!iconsFile.open("icons.ph")) {
     287                error("Groovie::Cursor: Couldn't open icons.ph");
     288                return;
     289        }
     290
     291        // Verify the signature
     292        uint32 tmp32 = iconsFile.readUint32LE();
     293        uint16 tmp16 = iconsFile.readUint16LE();
     294        if (tmp32 != 0x6e6f6369 || tmp16 != 1) {
     295                error("Groovie::Cursor: icons.ph signature failed: %04X %d", tmp32, tmp16);
     296                return;
     297        }
     298
     299        // Read the number of icons
     300        uint16 nicons = iconsFile.readUint16LE();
     301
     302        // Read the icons
     303        for (int i = 0; i < nicons; i++) {
     304                Cursor *s = new Cursor_v2(iconsFile);
     305                _cursors.push_back(s);
     306        }
     307
     308        iconsFile.close();
     309}
     310
     311CursorMan_v2::~CursorMan_v2() {
     312}
     313
     314} // End of Groovie namespace
  • engines/groovie/detection.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/groovie/detection.cpp $
     22 * $Id: detection.cpp 28132 2007-07-17 22:29:09Z aquadran $
     23 *
     24 */
     25
     26#include "common/savefile.h"
     27
     28#include "groovie/groovie.h"
     29
     30namespace Groovie {
     31
     32//#define GROOVIE_EXPERIMENTAL
     33
     34static const PlainGameDescriptor groovieGames[] = {
     35        // Games
     36        {"t7g", "The 7th Guest"},
     37
     38#ifdef GROOVIE_EXPERIMENTAL
     39        {"11h", "The 11th Hour: The sequel to The 7th Guest"},
     40        {"clandestiny", "Clandestiny"},
     41        {"unclehenry", "Uncle Henry's Playhouse"},
     42        {"tlc", "Tender Loving Care"},
     43
     44        // Extras
     45        {"making11h", "The Making of The 11th Hour"},
     46        {"clantrailer", "Clandestiny Trailer"},
     47#endif
     48
     49        // Unknown
     50        {"groovie", "Groovie engine game"},
     51        {0, 0}
     52};
     53
     54static const GroovieGameDescription gameDescriptions[] = {
     55
     56        // The 7th Guest DOS English
     57        {
     58                {
     59                        "t7g", "",
     60                        AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659),
     61                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     62                },
     63                kGroovieT7G, 0
     64        },
     65
     66        // The 7th Guest Mac English
     67        {
     68                {
     69                        "t7g", "",
     70                        AD_ENTRY1s("script.grv", "6e30b54b1f3bc2262cdcf7961db2ae67", 17191),
     71                        Common::EN_ANY, Common::kPlatformMacintosh, Common::ADGF_NO_FLAGS
     72                },
     73                kGroovieT7G, 0
     74        },
     75
     76#ifdef GROOVIE_EXPERIMENTAL
     77        // The 11th Hour DOS English
     78        {
     79                {
     80                        "11h", "",
     81                        AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
     82                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     83                },
     84                kGroovieV2, 1
     85        },
     86
     87        // The Making of The 11th Hour DOS English
     88        {
     89                {
     90                        "making11h", "",
     91                        AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
     92                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     93                },
     94                kGroovieV2, 2
     95        },
     96
     97        // Clandestiny Trailer DOS English
     98        {
     99                {
     100                        "clantrailer", "",
     101                        AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
     102                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     103                },
     104                kGroovieV2, 3
     105        },
     106
     107        // Clandestiny DOS English
     108        {
     109                {
     110                        "clandestiny", "",
     111                        AD_ENTRY1s("disk.1", "f79fc1515174540fef6a34132efc4c53", 76),
     112                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     113                },
     114                kGroovieV2, 1
     115        },
     116
     117        // Uncle Henry's Playhouse PC English
     118        {
     119                {
     120                        "unclehenry", "",
     121                        AD_ENTRY1s("disk.1", "0e1b1d3cecc4fc7efa62a968844d1f7a", 72),
     122                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     123                },
     124                kGroovieV2, 1
     125        },
     126
     127        // Tender Loving Care PC English
     128        {
     129                {
     130                        "tlc", "",
     131                        AD_ENTRY1s("disk.1", "32a1afa68478f1f9d2b25eeea427f2e3", 84),
     132                        Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
     133                },
     134                kGroovieV2, 1
     135        },
     136#endif
     137
     138        {AD_TABLE_END_MARKER, kGroovieT7G, 0}
     139};
     140
     141static const Common::ADParams detectionParams = {
     142        // Pointer to ADGameDescription or its superset structure
     143        (const byte *)gameDescriptions,
     144        // Size of that superset structure
     145        sizeof(GroovieGameDescription),
     146        // Number of bytes to compute MD5 sum for
     147        5000,
     148        // List of all engine targets
     149        groovieGames,
     150        // Structure for autoupgrading obsolete targets
     151        0,
     152        // Name of single gameid (optional)
     153        0,
     154        // List of files for file-based fallback detection (optional)
     155        0,
     156        // Flags
     157        0
     158};
     159
     160
     161class GroovieMetaEngine : public Common::AdvancedMetaEngine {
     162public:
     163        GroovieMetaEngine() : Common::AdvancedMetaEngine(detectionParams) {}
     164
     165        const char *getName() const {
     166                return "Groovie Engine";
     167        }
     168
     169        const char *getCopyright() const {
     170                return "Groovie Engine (C) 1990-1996 Trilobyte";
     171        }
     172
     173        bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const;
     174
     175        bool hasFeature(MetaEngineFeature f) const;
     176        SaveStateList listSaves(const char *target) const;
     177        void removeSaveState(const char *target, int slot) const;
     178};
     179
     180bool GroovieMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const {
     181        if (gd) {
     182                *engine = new GroovieEngine(syst, (GroovieGameDescription *)gd);
     183        }
     184        return gd != 0;
     185}
     186
     187bool GroovieMetaEngine::hasFeature(MetaEngineFeature f) const {
     188        return
     189                (f == kSupportsListSaves) ||
     190                (f == kSupportsLoadingDuringStartup) ||
     191                (f == kSupportsDeleteSave);
     192                //(f == kSavesSupportCreationDate)
     193}
     194
     195SaveStateList GroovieMetaEngine::listSaves(const char *target) const {
     196        Common::SaveFileManager *sfm = g_system->getSavefileManager();
     197        SaveStateList list;
     198
     199        // Get the list of savefiles
     200        Common::String pattern = Common::String(target) + ".00?";
     201        Common::StringList savefiles = sfm->listSavefiles(pattern.c_str());
     202
     203        // Sort the list of filenames
     204        sort(savefiles.begin(), savefiles.end());
     205
     206        // Fill the information for the existing savegames
     207        Common::StringList::iterator it = savefiles.begin();
     208        while (it != savefiles.end()) {
     209                int slot = it->lastChar() - '0';
     210                if (slot >= 0 && slot <= 9) {
     211                        Common::InSaveFile *file = sfm->openForLoading(it->c_str());
     212
     213                        // Read the savegame description
     214                        Common::String description;
     215                        unsigned char c = 1;
     216                        for (int i = 0; (c != 0) && (i < 15); i++) {
     217                                c = file->readByte();
     218                                switch (c) {
     219                                case 0:
     220                                        break;
     221                                case 16: // @
     222                                        c = ' ';
     223                                        break;
     224                                case 244: // $
     225                                        c = 0;
     226                                        break;
     227                                default:
     228                                        c += 0x30;
     229                                }
     230                                if (c != 0) {
     231                                        description += c;
     232                                }
     233                        }
     234                        delete file;
     235
     236                        list.push_back(SaveStateDescriptor(slot, description));
     237                }
     238                it++;
     239        }
     240
     241        return list;
     242}
     243
     244void GroovieMetaEngine::removeSaveState(const char *target, int slot) const {
     245        if (slot < 0 || slot > 9) {
     246                // Invalid slot, do nothing
     247                return;
     248        }
     249
     250        char extension[6];
     251        snprintf(extension, sizeof(extension), ".00%01d", slot);
     252
     253        Common::String filename = target;
     254        filename += extension;
     255
     256        g_system->getSavefileManager()->removeSavefile(filename.c_str());
     257}
     258
     259} // End of namespace Groovie
     260
     261#if PLUGIN_ENABLED_DYNAMIC(GROOVIE)
     262        REGISTER_PLUGIN_DYNAMIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
     263#else
     264        REGISTER_PLUGIN_STATIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
     265#endif
  • engines/groovie/groovie.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_H
     27#define GROOVIE_H
     28
     29#include "common/advancedDetector.h"
     30#include "engines/engine.h"
     31#include "graphics/surface.h"
     32
     33#include "groovie/cursor.h"
     34#include "groovie/debug.h"
     35#include "groovie/graphics.h"
     36#include "groovie/player.h"
     37#include "groovie/resource.h"
     38#include "groovie/script.h"
     39
     40namespace Groovie {
     41
     42class MusicPlayer;
     43
     44enum kDebugLevels {
     45        kGroovieDebugAll = 1 << 0,
     46        kGroovieDebugVideo = 1 << 1,
     47        kGroovieDebugResource = 1 << 2,
     48        kGroovieDebugScript = 1 << 3,
     49        kGroovieDebugUnknown = 1 << 4,
     50        kGroovieDebugHotspots = 1 << 5,
     51        kGroovieDebugCursor = 1 << 6
     52                // the current limitation is 32 debug levels (1 << 31 is the last one)
     53};
     54
     55enum kEngineVersion {
     56        kGroovieT7G,
     57        kGroovieV2
     58};
     59
     60struct GroovieGameDescription {
     61        Common::ADGameDescription desc;
     62
     63        kEngineVersion version; // Version of the engine
     64        int indexEntry; // The index of the entry in disk.1 for V2 games
     65};
     66
     67class GroovieEngine : public Engine {
     68public:
     69        GroovieEngine(OSystem *syst, GroovieGameDescription *gd);
     70        ~GroovieEngine();
     71
     72protected:
     73        Common::Error init();
     74        Common::Error go();
     75
     76public:
     77        bool hasFeature(EngineFeature f) const;
     78
     79        bool canLoadGameStateCurrently();
     80        Common::Error loadGameState(int slot);
     81        void syncSoundSettings();
     82
     83        Debugger *getDebugger() { return _debugger; }
     84
     85        void waitForInput();
     86
     87        Script _script;
     88        ResMan *_resMan;
     89        CursorMan *_cursorMan;
     90        VideoPlayer *_videoPlayer;
     91        MusicPlayer *_musicPlayer;
     92        GraphicsMan *_graphicsMan;
     93
     94private:
     95        GroovieGameDescription *_gameDescription;
     96        Debugger *_debugger;
     97        bool _waitingForInput;
     98};
     99
     100} // End of namespace Groovie
     101
     102#endif // GROOVIE_H
  • engines/groovie/music.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/music.h"
     27#include "groovie/resource.h"
     28
     29namespace Groovie {
     30
     31MusicPlayer::MusicPlayer(GroovieEngine *vm) :
     32        _vm(vm), _midiParser(NULL), _data(NULL), _driver(NULL),
     33        _backgroundFileRef(0) {
     34        // Create the parser
     35        _midiParser = MidiParser::createParser_XMIDI();
     36
     37        // Create the driver
     38        int driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
     39        _driver = createMidi(driver);
     40        _driver->open();
     41
     42        // Initialize the channel volumes
     43        for (int i = 0; i < 0x10; i++) {
     44                _chanVolumes[i] = 0x7F;
     45        }
     46
     47        // Set the parser's driver
     48        _midiParser->setMidiDriver(this);
     49
     50        // Set the timer rate
     51        _midiParser->setTimerRate(_driver->getBaseTempo());
     52}
     53
     54MusicPlayer::~MusicPlayer() {
     55        // Unload the parser
     56        unload();
     57        delete _midiParser;
     58
     59        // Unload the MIDI Driver
     60        _driver->close();
     61        delete _driver;
     62}
     63
     64void MusicPlayer::playSong(uint16 fileref) {
     65        // Play the referenced file once
     66        play(fileref, false);
     67}
     68
     69void MusicPlayer::setBackgroundSong(uint16 fileref) {
     70        _backgroundFileRef = fileref;
     71}
     72
     73void MusicPlayer::setUserVolume(uint16 volume) {
     74        // Save the new user volume
     75        _userVolume = volume;
     76        if (_userVolume > 0x100) _userVolume = 0x100;
     77
     78        // Apply it to all the channels
     79        for (int i = 0; i < 0x10; i++) {
     80                updateChanVolume(i);
     81        }
     82        //FIXME: AdlibPercussionChannel::controlChange() is empty
     83        //(can't set the volume for the percusion channel)
     84}
     85
     86void MusicPlayer::setGameVolume(uint16 volume, uint16 time) {
     87        //TODO: Implement volume fading
     88        printf("setting game volume: %d, %d\n", volume, time);
     89
     90        // Save the new game volume
     91        _gameVolume = volume;
     92        if (_gameVolume > 100) _gameVolume = 100;
     93
     94        // Apply it to all the channels
     95        for (int i = 0; i < 0x10; i++) {
     96                updateChanVolume(i);
     97        }
     98}
     99
     100void MusicPlayer::updateChanVolume(byte channel) {
     101        // Generate a MIDI Control change message for the volume
     102        uint32 b = 0x7B0;
     103
     104        // Specify the channel
     105        b |= (channel & 0xF);
     106
     107        // Scale by the user and game volumes
     108        uint32 val = (_chanVolumes[channel] * _userVolume * _gameVolume) / 0x100 / 100;
     109        val &= 0x7F;
     110
     111        // Send it to the driver
     112        _driver->send(b | (val << 16));
     113}
     114
     115bool MusicPlayer::play(uint16 fileref, bool loop) {
     116        // Unload the previous song
     117        unload();
     118
     119        // Set the looping option
     120        _midiParser->property(MidiParser::mpAutoLoop, loop);
     121
     122        // Load the new file
     123        return load(fileref);
     124}
     125
     126bool MusicPlayer::load(uint16 fileref) {
     127        // Open the song resource
     128        Common::SeekableReadStream *xmidiFile = _vm->_resMan->open(fileref);
     129        if (!xmidiFile) {
     130                error("Groovie::Music: Couldn't resource 0x%04X", fileref);
     131                return false;
     132        }
     133
     134        // Read the whole file to memory
     135        int length = xmidiFile->size();
     136        _data = new byte[length];
     137        xmidiFile->read(_data, length);
     138        delete xmidiFile;
     139
     140        // Start parsing the data
     141        if (!_midiParser->loadMusic(_data, length)) {
     142                error("Groovie::Music: Invalid XMI file");
     143                return false;
     144        }
     145
     146        // Activate the timer source
     147        _driver->setTimerCallback(_midiParser, MidiParser::timerCallback);
     148
     149        return true;
     150}
     151
     152void MusicPlayer::unload() {
     153        // Unload the parser
     154        _midiParser->unloadMusic();
     155
     156        // Unload the xmi file
     157        delete[] _data;
     158        _data = NULL;
     159}
     160
     161int MusicPlayer::open() {
     162        return 0;
     163}
     164
     165void MusicPlayer::close() {}
     166
     167void MusicPlayer::send(uint32 b) {
     168        if ((b & 0xFFF0) == 0x07B0) { // Volume change
     169                // Save the specific channel volume
     170                byte chan = b & 0xF;
     171                _chanVolumes[chan] = (b >> 16) & 0x7F;
     172
     173                // Send the updated value
     174                updateChanVolume(chan);
     175
     176                return;
     177        }
     178        _driver->send(b);
     179}
     180
     181void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
     182        switch (type) {
     183        case 0x2F:
     184                // End of Track, play the background song
     185                if (_backgroundFileRef) {
     186                        play(_backgroundFileRef, true);
     187                }
     188                break;
     189        default:
     190                _driver->metaEvent(type, data, length);
     191                break;
     192        }
     193}
     194
     195void MusicPlayer::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
     196        _driver->setTimerCallback(timer_param, timer_proc);
     197}
     198
     199uint32 MusicPlayer::getBaseTempo(void) {
     200        return _driver->getBaseTempo();
     201}
     202
     203MidiChannel *MusicPlayer::allocateChannel() {
     204        return _driver->allocateChannel();
     205}
     206
     207MidiChannel *MusicPlayer::getPercussionChannel() {
     208        return _driver->getPercussionChannel();
     209}
     210
     211} // End of Groovie namespace
  • engines/groovie/cursor.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_CURSOR_H
     27#define GROOVIE_CURSOR_H
     28
     29#include "common/system.h"
     30#include "common/file.h"
     31
     32namespace Groovie {
     33
     34class Cursor {
     35public:
     36        virtual ~Cursor() {}
     37        uint16 getFrames() { return _numFrames; }
     38        virtual void enable() = 0;
     39        virtual void showFrame(uint16 frame) = 0;
     40
     41protected:
     42        uint16 _width;
     43        uint16 _height;
     44        uint16 _numFrames;
     45};
     46
     47class CursorMan {
     48public:
     49        CursorMan(OSystem *system);
     50        virtual ~CursorMan();
     51
     52        virtual void animate();
     53        virtual void setStyle(uint8 newStyle);
     54        virtual uint8 getStyle();
     55
     56protected:
     57        OSystem *_syst;
     58
     59        // Animation variables
     60        uint8 _lastFrame;
     61        uint32 _lastTime;
     62
     63        // Styles
     64        Common::Array<Cursor *> _cursors;
     65        uint8 _current;
     66        Cursor *_cursor;
     67};
     68
     69class CursorMan_t7g : public CursorMan {
     70public:
     71        CursorMan_t7g(OSystem *system);
     72        ~CursorMan_t7g();
     73
     74private:
     75        // Styles data
     76        static const uint _cursorImg[];
     77        static const uint _cursorPal[];
     78
     79        // Cursors data
     80        Common::Array<byte *> _images;
     81        Common::Array<byte *> _palettes;
     82
     83        // Loading functions
     84        byte *loadImage(Common::File &file);
     85        byte *loadPalette(Common::File &file);
     86};
     87
     88class CursorMan_v2 : public CursorMan {
     89public:
     90        CursorMan_v2(OSystem *system);
     91        ~CursorMan_v2();
     92};
     93
     94} // End of Groovie namespace
     95
     96#endif // GROOVIE_CURSOR_H
  • engines/groovie/music.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_MUSIC_H
     27#define GROOVIE_MUSIC_H
     28
     29#include "groovie/groovie.h"
     30
     31#include "sound/mididrv.h"
     32#include "sound/midiparser.h"
     33
     34namespace Groovie {
     35
     36class MusicPlayer : public MidiDriver {
     37public:
     38        MusicPlayer(GroovieEngine *vm);
     39        ~MusicPlayer();
     40        void playSong(uint16 fileref);
     41        void setBackgroundSong(uint16 fileref);
     42
     43        // Volume
     44        void setUserVolume(uint16 volume);
     45        void setGameVolume(uint16 volume, uint16 time);
     46private:
     47        uint16 _userVolume;
     48        uint16 _gameVolume;
     49        byte _chanVolumes[0x10];
     50        void updateChanVolume(byte channel);
     51
     52public:
     53        // MidiDriver interface
     54        int open();
     55        void close();
     56        void send(uint32 b);
     57        void metaEvent(byte type, byte *data, uint16 length);
     58        void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
     59        uint32 getBaseTempo(void);
     60        MidiChannel *allocateChannel();
     61        MidiChannel *getPercussionChannel();
     62
     63private:
     64        GroovieEngine *_vm;
     65        byte *_data;
     66        MidiParser *_midiParser;
     67        MidiDriver *_driver;
     68
     69        uint16 _backgroundFileRef;
     70
     71        bool play(uint16 fileref, bool loop);
     72        bool load(uint16 fileref);
     73        void unload();
     74};
     75
     76} // End of Groovie namespace
     77
     78#endif // GROOVIE_MUSIC_H
  • engines/groovie/jpeg.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/jpeg.h"
     28
     29#include "common/util.h"
     30
     31namespace Groovie {
     32
     33// Order used to traverse the quantization tables
     34uint8 JPEG::_zigZagOrder[64] = {
     35        0,   1,  8, 16,  9,  2,  3, 10,
     36        17, 24, 32, 25, 18, 11,  4,  5,
     37        12, 19, 26, 33, 40, 48, 41, 34,
     38        27, 20, 13,  6,  7, 14, 21, 28,
     39        35, 42, 49, 56, 57, 50, 43, 36,
     40        29, 22, 15, 23, 30, 37, 44, 51,
     41        58, 59, 52, 45, 38, 31, 39, 46,
     42        53, 60, 61, 54, 47, 55, 62, 63
     43};
     44
     45JPEG::JPEG() :
     46        _str(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
     47        _scanComp(NULL), _currentComp(NULL) {
     48
     49        // Initialize the quantization tables
     50        for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
     51                _quant[i] = NULL;
     52        }
     53
     54        // Initialize the Huffman tables
     55        for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
     56                _huff[i].count = 0;
     57                _huff[i].values = NULL;
     58                _huff[i].sizes = NULL;
     59                _huff[i].codes = NULL;
     60        }
     61}
     62
     63JPEG::~JPEG() {
     64        reset();
     65}
     66
     67void JPEG::reset() {
     68        // Reset member variables
     69        _str = NULL;
     70        _w = _h = 0;
     71
     72        // Free the components
     73        if (_components) {
     74                delete[] _components;
     75                _components = NULL;
     76        }
     77        _numComp = 0;
     78
     79        // Free the scan components
     80        if (_scanComp) {
     81                delete[] _scanComp;
     82                _scanComp = NULL;
     83        }
     84        _numScanComp = 0;
     85        _currentComp = NULL;
     86
     87        // Free the quantization tables
     88        for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
     89                if (_quant[i]) {
     90                        delete[] _quant[i];
     91                        _quant[i] = NULL;
     92                }
     93        }
     94
     95        // Free the Huffman tables
     96        for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
     97                _huff[i].count = 0;
     98                if (_huff[i].values) {
     99                        delete[] _huff[i].values;
     100                        _huff[i].values = NULL;
     101                }
     102                if (_huff[i].sizes) {
     103                        delete[] _huff[i].sizes;
     104                        _huff[i].sizes = NULL;
     105                }
     106                if (_huff[i].codes) {
     107                        delete[] _huff[i].codes;
     108                        _huff[i].codes = NULL;
     109                }
     110        }
     111}
     112
     113bool JPEG::read(Common::ReadStream *str) {
     114        // Reset member variables and tables from previous reads
     115        reset();
     116
     117        // Save the input stream
     118        _str = str;
     119
     120        bool ok = true;
     121        while (!_str->eos() && ok) {
     122                // Read the marker
     123                uint16 marker = _str->readByte();
     124                if (marker != 0xFF) {
     125                        error("Groovie::JPEG: Invalid marker[0]: 0x%02X", marker);
     126                        ok = false;
     127                        break;
     128                }
     129                while (marker == 0xFF) {
     130                        marker = _str->readByte();
     131                }
     132
     133                // Process the marker data
     134                switch (marker) {
     135                case 0xC0: // Start Of Frame
     136                        ok = readSOF0();
     137                        break;
     138                case 0xC4: // Define Huffman Tables
     139                        ok = readDHT();
     140                        break;
     141                case 0xD8: // Start Of Image
     142                case 0xD9: // End Of Image
     143                        break;
     144                case 0xDA: // Start Of Scan
     145                        ok = readSOS();
     146                        break;
     147                case 0xDB: // Define Quantization Tables
     148                        ok = readDQT();
     149                        break;
     150                default: { // Unknown marker
     151                        uint16 size = _str->readUint16BE();
     152                        warning("Groovie::JPEG: Unknown marker %02X, skipping %d bytes", marker, size);
     153                        for (int i = 2; i < size; i++) {
     154                                _str->readByte();
     155                        }
     156                }
     157                }
     158        }
     159        return ok;
     160}
     161
     162// Marker 0xC0 (Start Of Frame, Baseline DCT)
     163bool JPEG::readSOF0() {
     164        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::JPEG: readSOF0");
     165        uint16 size = _str->readUint16BE();
     166
     167        // Read the sample precision
     168        uint8 precision = _str->readByte();
     169        if (precision != 8) {
     170                warning("Groovie::JPEG: Just 8 bit precision supported at the moment");
     171                return false;
     172        }
     173
     174        // Image size
     175        _h = _str->readUint16BE();
     176        _w = _str->readUint16BE();
     177
     178        // Number of components
     179        _numComp = _str->readByte();
     180        if (size != 8 + 3 * _numComp) {
     181                warning("Groovie::JPEG: Invalid number of components");
     182                return false;
     183        }
     184
     185        // Allocate the new components
     186        if (_components) {
     187                delete[] _components;
     188        }
     189        _components = new Component[_numComp];
     190
     191        // Read the components details
     192        for (int c = 0; c < _numComp; c++) {
     193                _components[c].id = _str->readByte();
     194                _components[c].factorH = _str->readByte();
     195                _components[c].factorV = _components[c].factorH & 0xF;
     196                _components[c].factorH >>= 4;
     197                _components[c].quantTableSelector = _str->readByte();
     198        }
     199
     200        return true;
     201}
     202
     203// Marker 0xC4 (Define Huffman Tables)
     204bool JPEG::readDHT() {
     205        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::JPEG: readDHT");
     206        uint16 size = _str->readUint16BE();
     207
     208        // Read the table type and id
     209        uint8 tableId = _str->readByte();
     210        uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
     211        tableId &= 0xF;
     212        uint8 tableNum = (tableId << 1) + tableType;
     213
     214        // Free the Huffman table
     215        if (_huff[tableNum].values) {
     216                delete[] _huff[tableNum].values;
     217                _huff[tableNum].values = NULL;
     218        }
     219        if (_huff[tableNum].sizes) {
     220                delete[] _huff[tableNum].sizes;
     221                _huff[tableNum].sizes = NULL;
     222        }
     223        if (_huff[tableNum].codes) {
     224                delete[] _huff[tableNum].codes;
     225                _huff[tableNum].codes = NULL;
     226        }
     227
     228        // Read the number of values for each length
     229        uint8 numValues[16];
     230        _huff[tableNum].count = 0;
     231        for (int len = 0; len < 16; len++) {
     232                numValues[len] = _str->readByte();
     233                _huff[tableNum].count += numValues[len];
     234        }
     235
     236        // Verify the number of bytes to read
     237        if (size != _huff[tableNum].count + 19) {
     238                warning("Groovie::JPEG: Invalid number of values in the Huffman table");
     239                return false;
     240        }
     241
     242        // Allocate memory for the current table
     243        _huff[tableNum].values = new uint8[_huff[tableNum].count];
     244        _huff[tableNum].sizes = new uint8[_huff[tableNum].count];
     245        _huff[tableNum].codes = new uint16[_huff[tableNum].count];
     246
     247        // Read the table contents
     248        int cur = 0;
     249        for (int len = 0; len < 16; len++) {
     250                for (int i = 0; i < numValues[len]; i++) {
     251                        _huff[tableNum].values[cur] = _str->readByte();
     252                        _huff[tableNum].sizes[cur] = len + 1;
     253                        cur++;
     254                }
     255        }
     256
     257        // Fill the table of Huffman codes
     258        cur = 0;
     259        uint16 curCode = 0;
     260        uint8 curCodeSize = _huff[tableNum].sizes[0];
     261        while (cur < _huff[tableNum].count) {
     262                // Increase the code size to fit the request
     263                while (_huff[tableNum].sizes[cur] != curCodeSize) {
     264                        curCode <<= 1;
     265                        curCodeSize++;
     266                }
     267
     268                // Assign the current code
     269                _huff[tableNum].codes[cur] = curCode;
     270                curCode++;
     271                cur++;
     272        }
     273
     274        return true;
     275}
     276
     277// Marker 0xDA (Start Of Scan)
     278bool JPEG::readSOS() {
     279        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::JPEG: readSOS");
     280        uint16 size = _str->readUint16BE();
     281
     282        // Number of scan components
     283        _numScanComp = _str->readByte();
     284        if (size != 6 + 2 * _numScanComp) {
     285                warning("Groovie::JPEG: Invalid number of components");
     286                return false;
     287        }
     288
     289        // Allocate the new scan components
     290        if (_scanComp) {
     291                delete[] _scanComp;
     292        }
     293        _scanComp = new Component *[_numScanComp];
     294
     295        // Reset the maximum sampling factors
     296        _maxFactorV = 0;
     297        _maxFactorH = 0;
     298
     299        // Component-specification parameters
     300        for (int c = 0; c < _numScanComp; c++) {
     301                // Read the desired component id
     302                uint8 id = _str->readByte();
     303
     304                // Search the component with the specified id
     305                bool found = false;
     306                for (int i = 0; !found && i < _numComp; i++) {
     307                        if (_components[i].id == id) {
     308                                // We found the desired component
     309                                found = true;
     310
     311                                // Assign the found component to the c'th scan component
     312                                _scanComp[c] = &_components[i];
     313                        }
     314                }
     315                if (!found) {
     316                        warning("Groovie::JPEG: Invalid component");
     317                        return false;
     318                }
     319
     320                // Read the entropy table selectors
     321                _scanComp[c]->DCentropyTableSelector = _str->readByte();
     322                _scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
     323                _scanComp[c]->DCentropyTableSelector >>= 4;
     324
     325                // Calculate the maximum sampling factors
     326                if (_scanComp[c]->factorV > _maxFactorV) {
     327                        _maxFactorV = _scanComp[c]->factorV;
     328                }
     329                if (_scanComp[c]->factorH > _maxFactorH) {
     330                        _maxFactorH = _scanComp[c]->factorH;
     331                }
     332
     333                // Initialize the DC predictor
     334                _scanComp[c]->DCpredictor = 0;
     335        }
     336
     337        // Start of spectral selection
     338        if (_str->readByte() != 0) {
     339                warning("Groovie::JPEG: Progressive scanning not supported");
     340                return false;
     341        }
     342
     343        // End of spectral selection
     344        if (_str->readByte() != 63) {
     345                warning("Groovie::JPEG: Progressive scanning not supported");
     346                return false;
     347        }
     348
     349        // Successive approximation parameters
     350        if (_str->readByte() != 0) {
     351                warning("Groovie::JPEG: Progressive scanning not supported");
     352                return false;
     353        }
     354
     355        // Entropy coded sequence starts, initialize Huffman decoder
     356        _bitsNumber = 0;
     357
     358        // Read all the scan MCUs
     359        uint16 xMCU = _w / (_maxFactorH * 8);
     360        uint16 yMCU = _h / (_maxFactorV * 8);
     361        bool ok = true;
     362        for (int y = 0; ok && (y < yMCU); y++) {
     363                for (int x = 0; ok && (x < xMCU); x++) {
     364                        ok = readMCU(x, y);
     365                }
     366        }
     367
     368        return ok;
     369}
     370
     371// Marker 0xDB (Define Quantization Tables)
     372bool JPEG::readDQT() {
     373        debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::JPEG: readDQT");
     374        uint16 size = _str->readUint16BE();
     375        if (size - 3 != 64) {
     376                warning("Groovie::JPEG: (TODO) Trying to define several quantization tables on the same block");
     377                return false;
     378        }
     379
     380        // Read the table precision and id
     381        uint8 tableId = _str->readByte();
     382        if (tableId & 0xF0) {
     383                // Precision = 1 -> 16 bits per element
     384                warning("Groovie::JPEG: Just 8 bit precision supported at the moment");
     385                return false;
     386        }
     387
     388        // Validate the table id
     389        tableId &= 0xF;
     390        if (tableId > JPEG_MAX_QUANT_TABLES) {
     391                warning("Groovie::JPEG: Invalid number of components");
     392                return false;
     393        }
     394
     395        // Create the new table if necessary
     396        if (!_quant[tableId]) {
     397                _quant[tableId] = new uint8[64];
     398        }
     399
     400        // Read the table in Zig-Zag order
     401        for (int i = 0; i < 64; i++) {
     402                _quant[tableId][_zigZagOrder[i]] = _str->readByte();
     403        }
     404
     405        return true;
     406}
     407
     408bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) {
     409        bool ok = true;
     410        for (int c = 0; ok && (c < _numComp); c++) {
     411                // Set the current component
     412                _currentComp = _scanComp[c];
     413
     414                // Read the data units of the current component
     415                for (int y = 0; ok && (y < _scanComp[c]->factorV); y++) {
     416                        for (int x = 0; ok && (x < _scanComp[c]->factorH); x++) {
     417                                ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
     418                        }
     419                }
     420        }
     421        //printf("MCU(%d,%d) ", xMCU, yMCU);
     422
     423        return ok;
     424}
     425
     426bool JPEG::readDataUnit(uint16 x, uint16 y) {
     427        // Prepare an empty data array
     428        int8 readData[64];
     429        for (int i = 1; i < 64; i++) {
     430                readData[i] = 0;
     431        }
     432
     433        // Read the DC component
     434        readData[0] = _currentComp->DCpredictor + readDC();
     435        _currentComp->DCpredictor = readData[0];
     436
     437        // Read the AC components
     438        readAC(readData);
     439
     440        // Calculate the DCT coefficients from the input sequence
     441        uint16 _DCT[64];
     442        for (int i = 0; i < 64; i++) {
     443                // Dequantize
     444                int16 val = readData[i];
     445                int16 quant = _quant[_currentComp->quantTableSelector][i];
     446                val *= quant;
     447
     448                // Store the normalized coefficients in the Zig-Zag order
     449                _DCT[_zigZagOrder[i]] = val;
     450        }
     451
     452        // TODO: apply the IDCT PAG31
     453
     454        // Level shift to make the values unsigned
     455        for (int i = 0; i < 64; i++) {
     456                _DCT[i] += 128;
     457        }
     458
     459        if (_currentComp->id == 1) {
     460                // HACK: show the DC luminance!
     461                for (int j = 0; j < 8 * _maxFactorV / _currentComp->factorV; j++) {
     462                        for (int i = 0; i < 8 * _maxFactorH / _currentComp->factorH; i++) {
     463                                // TODO
     464                                // paint (x + i, y + j)
     465                        }
     466                }
     467        }
     468
     469        return true;
     470}
     471
     472int8 JPEG::readDC() {
     473        // DC is type 0
     474        uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
     475
     476        // Get the number of bits to read
     477        uint8 numBits = readHuff(tableNum);
     478
     479        // Read the requested bits
     480        return readSignedBits(numBits);
     481}
     482
     483void JPEG::readAC(int8 *out) {
     484        // AC is type 1
     485        uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
     486
     487        // Start reading AC element 1
     488        uint8 cur = 1;
     489        while (cur < 64) {
     490                uint8 s = readHuff(tableNum);
     491                uint8 r = s >> 4;
     492                s &= 0xF;
     493
     494                if (s == 0) {
     495                        if (r == 15) {
     496                                // Skip 16 values
     497                                cur += 16;
     498                        } else {
     499                                // EOB: end of block
     500                                cur = 64;
     501                        }
     502                } else {
     503                        // Skip r values
     504                        cur += r;
     505
     506                        // Read the next value
     507                        out[cur] = readSignedBits(s);
     508                        cur++;
     509                }
     510        }
     511}
     512
     513int16 JPEG::readSignedBits(uint8 numBits) {
     514        uint16 ret = 0;
     515        if (numBits > 8) error("requested %d bits", numBits); //XXX
     516
     517        //MSB=0 for negatives, 1 for positives
     518        for (int i = 0; i < numBits; i++) {
     519                ret = (ret << 1) + readBit();
     520        }
     521
     522        // TODO: extend sign bits: PAG109
     523
     524        return ret;
     525}
     526
     527// TODO: optimize?
     528uint8 JPEG::readHuff(uint8 table) {
     529        bool foundCode = false;
     530        uint8 val = 0;
     531
     532        uint8 cur = 0;
     533        uint8 codeSize = 1;
     534        uint16 code = readBit();
     535        while (!foundCode) {
     536                // Prepare a code of the current size
     537                while (codeSize < _huff[table].sizes[cur]) {
     538                        code = (code << 1) + readBit();
     539                        codeSize++;
     540                }
     541
     542                // Compare the codes of the current size
     543                while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
     544                        if (code == _huff[table].codes[cur]) {
     545                                // Found the code
     546                                val = _huff[table].values[cur];
     547                                foundCode = true;
     548                        } else {
     549                                // Continue reading
     550                                cur++;
     551                        }
     552                }
     553        }
     554
     555        return val;
     556}
     557
     558uint8 JPEG::readBit() {
     559        // Read a whole byte if necessary
     560        if (_bitsNumber == 0) {
     561                _bitsData = _str->readByte();
     562                _bitsNumber = 8;
     563
     564                // Detect markers
     565                if (_bitsData == 0xFF) {
     566                        uint8 byte2 = _str->readByte();
     567
     568                        // A stuffed 0 validates the previous byte
     569                        if (byte2 != 0) {
     570                                if (byte2 == 0xDC) {
     571                                        // DNL marker: Define Number of Lines
     572                                        // TODO: terminate scan
     573                                        printf("DNL marker detected: terminate scan\n");
     574                                } else {
     575                                        printf("Error: marker 0x%02X read in entropy data\n", byte2);
     576                                }
     577                        }
     578                }
     579        }
     580        _bitsNumber--;
     581
     582        return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
     583}
     584
     585} // End of Groovie namespace
  • engines/groovie/debug.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/debug.h"
     27#include "groovie/script.h"
     28#include "groovie/groovie.h"
     29
     30namespace Groovie {
     31
     32Debugger::Debugger(GroovieEngine *vm) :
     33        _vm (vm), _script(&_vm->_script), _syst(_vm->_system) {
     34
     35        // Register the debugger comands
     36        DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
     37        DCmd_Register("go", WRAP_METHOD(Debugger, cmd_go));
     38        DCmd_Register("pc", WRAP_METHOD(Debugger, cmd_pc));
     39        DCmd_Register("fg", WRAP_METHOD(Debugger, cmd_fg));
     40        DCmd_Register("bg", WRAP_METHOD(Debugger, cmd_bg));
     41        DCmd_Register("mem", WRAP_METHOD(Debugger, cmd_mem));
     42        DCmd_Register("load", WRAP_METHOD(Debugger, cmd_loadgame));
     43        DCmd_Register("save", WRAP_METHOD(Debugger, cmd_savegame));
     44        DCmd_Register("playref", WRAP_METHOD(Debugger, cmd_playref));
     45        DCmd_Register("dumppal", WRAP_METHOD(Debugger, cmd_dumppal));
     46}
     47
     48Debugger::~Debugger() {
     49        Common::clearAllSpecialDebugLevels();
     50}
     51
     52int Debugger::getNumber(const char *arg) {
     53        return strtol(arg, (char **)NULL, 0);
     54}
     55
     56bool Debugger::cmd_step(int argc, const char **argv) {
     57        _script->step();
     58        return true;
     59}
     60
     61bool Debugger::cmd_go(int argc, const char **argv) {
     62        _script->step();
     63        return false;
     64}
     65
     66bool Debugger::cmd_fg(int argc, const char **argv) {
     67        _vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_foreground);
     68        return false;
     69}
     70
     71bool Debugger::cmd_bg(int argc, const char **argv) {
     72        _vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_background);
     73        return false;
     74}
     75
     76bool Debugger::cmd_pc(int argc, const char **argv) {
     77        if (argc == 2) {
     78                int val = getNumber(argv[1]);
     79                _script->_currentInstruction = val;
     80        }
     81        DebugPrintf("pc = 0x%04X\n", _script->_currentInstruction);
     82        return true;
     83}
     84
     85bool Debugger::cmd_mem(int argc, const char **argv) {
     86        if (argc >= 2) {
     87                int pos = getNumber(argv[1]);
     88                uint8 val;
     89                if (argc >= 3) {
     90                        // Set
     91                        val = getNumber(argv[2]);
     92                        _script->_variables[pos] = val;
     93                } else {
     94                        // Get
     95                        val = _script->_variables[pos];
     96                }
     97                DebugPrintf("mem[0x%04X] = 0x%02X\n", pos, val);
     98        } else {
     99                DebugPrintf("Syntax: mem <addr> [<val>]\n");
     100        }
     101        return true;
     102}
     103
     104bool Debugger::cmd_loadgame(int argc, const char **argv) {
     105        if (argc == 2) {
     106                int slot = getNumber(argv[1]);
     107                _script->loadgame(slot);
     108        } else {
     109                DebugPrintf("Syntax: load <slot>\n");
     110        }
     111        return true;
     112}
     113
     114bool Debugger::cmd_savegame(int argc, const char **argv) {
     115        if (argc == 2) {
     116                int slot = getNumber(argv[1]);
     117                _script->savegame(slot);
     118        } else {
     119                DebugPrintf("Syntax: save <slot>\n");
     120        }
     121        return true;
     122}
     123
     124bool Debugger::cmd_playref(int argc, const char **argv) {
     125        if (argc == 2) {
     126                int ref = getNumber(argv[1]);
     127                _script->playvideofromref(ref);
     128        } else {
     129                DebugPrintf("Syntax: playref <videorefnum>\n");
     130        }
     131        return true;
     132}
     133
     134bool Debugger::cmd_dumppal(int argc, const char **argv) {
     135        uint16 i;
     136        byte palettedump[256 * 4];
     137        _syst->grabPalette(palettedump, 0, 256);
     138
     139        for (i = 0; i < 256; i++) {
     140                printf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
     141                DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
     142        }
     143        return true;
     144}
     145
     146} // End of Groovie namespace
  • engines/groovie/resource.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/resource.h"
     28
     29namespace Groovie {
     30
     31// ResMan
     32
     33Common::SeekableReadStream *ResMan::open(uint16 fileRef) {
     34        // Get the information about the resource
     35        ResInfo resInfo;
     36        if (!getResInfo(fileRef, resInfo)) {
     37                return NULL;
     38        }
     39
     40        // Do we know the name of the required GJD?
     41        if (resInfo.gjd >= _gjds.size()) {
     42                error("Groovie::Resource: Unknown GJD %d", resInfo.gjd);
     43                return NULL;
     44        }
     45
     46        debugC(1, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Opening resource 0x%04X (%s, %d, %d)", fileRef, _gjds[resInfo.gjd].c_str(), resInfo.offset, resInfo.size);
     47
     48        // Does it exist?
     49        if (!Common::File::exists(_gjds[resInfo.gjd])) {
     50                error("Groovie::Resource: %s not found", _gjds[resInfo.gjd].c_str());
     51                return NULL;
     52        }
     53
     54        // Open the pack file
     55        Common::File *gjdFile = new Common::File();
     56        if (!gjdFile->open(_gjds[resInfo.gjd].c_str())) {
     57                delete gjdFile;
     58                error("Groovie::Resource: Couldn't open %s", _gjds[resInfo.gjd].c_str());
     59                return NULL;
     60        }
     61
     62        // Save the used gjd file (except xmi and gamwav)
     63        if (resInfo.gjd < 19) {
     64                _lastGjd = resInfo.gjd;
     65        }
     66
     67        // Returning the resource substream
     68        return new Common::SeekableSubReadStream(gjdFile, resInfo.offset, resInfo.offset + resInfo.size, true);
     69}
     70
     71
     72// ResMan_t7g
     73
     74static const char t7g_gjds[][0x15] = {"at", "b", "ch", "d", "dr", "fh", "ga", "hdisk", "htbd", "intro", "jhek", "k", "la", "li", "mb", "mc", "mu", "n", "p", "xmi", "gamwav"};
     75
     76ResMan_t7g::ResMan_t7g() {
     77        for (int i = 0; i < 0x15; i++) {
     78                // Prepare the filename
     79                Common::String filename = t7g_gjds[i];
     80                filename += ".gjd";
     81
     82                // Append it to the list of GJD files
     83                _gjds.push_back(filename);
     84        }
     85}
     86
     87uint16 ResMan_t7g::getRef(Common::String name, Common::String scriptname) {
     88        // Get the name of the RL file
     89        Common::String rlFileName(t7g_gjds[_lastGjd]);
     90        rlFileName += ".rl";
     91
     92        // Open the RL file
     93        Common::File rlFile;
     94        if (!rlFile.open(rlFileName)) {
     95                error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
     96                return false;
     97        }
     98
     99        uint16 resNum;
     100        bool found = false;
     101        for (resNum = 0; !found && !rlFile.ioFailed(); resNum++) {
     102                // Read the resource name
     103                char readname[12];
     104                rlFile.read(readname, 12);
     105
     106                // Test whether it's the resource we're searching
     107                Common::String resname(readname, 12);
     108                if (resname.hasPrefix(name.c_str())) {
     109                        debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource %12s matches %s", readname, name.c_str());
     110                        found = true;
     111                }
     112
     113                // Skip the rest of resource information
     114                rlFile.read(readname, 8);
     115        }
     116
     117        // Close the RL file
     118        rlFile.close();
     119
     120        // Verify we really found the resource
     121        if (!found) {
     122                error("Groovie::Resource: Couldn't find resource %s in %s", name.c_str(), rlFileName.c_str());
     123                return (uint16)-1;
     124        }
     125
     126        return (_lastGjd << 10) | (resNum - 1);
     127}
     128
     129bool ResMan_t7g::getResInfo(uint16 fileRef, ResInfo &resInfo) {
     130        // Calculate the GJD and the resource number
     131        resInfo.gjd = fileRef >> 10;
     132        uint16 resNum = fileRef & 0x3FF;
     133
     134        // Get the name of the RL file
     135        Common::String rlFileName(t7g_gjds[resInfo.gjd]);
     136        rlFileName += ".rl";
     137
     138        // Open the RL file
     139        Common::File rlFile;
     140        if (!rlFile.open(rlFileName)) {
     141                error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
     142                return false;
     143        }
     144
     145        // Seek to the position of the desired resource
     146        rlFile.seek(resNum * 20);
     147        if (rlFile.eos()) {
     148                rlFile.close();
     149                error("Groovie::Resource: Invalid resource number: 0x%04X (%s)", resNum, rlFileName.c_str());
     150                return false;
     151        }
     152
     153        // Read the resource name (just for debugging purposes)
     154        char resname[12];
     155        rlFile.read(resname, 12);
     156        debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
     157
     158        // Read the resource information
     159        resInfo.offset = rlFile.readUint32LE();
     160        resInfo.size = rlFile.readUint32LE();
     161
     162        // Close the resource RL file
     163        rlFile.close();
     164
     165        return true;
     166}
     167
     168
     169// ResMan_v2
     170
     171ResMan_v2::ResMan_v2() {
     172        Common::File indexfile;
     173
     174        // Open the GJD index file
     175        if (!indexfile.open("gjd.gjd")) {
     176                error("Groovie::Resource: Couldn't open gjd.gjd");
     177                return;
     178        }
     179
     180        Common::String line = indexfile.readLine();
     181        while (!indexfile.eos() && !line.empty()) {
     182                // Get the name before the space
     183                Common::String filename;
     184                for (const char *cur = line.c_str(); *cur != ' '; cur++) {
     185                        filename += *cur;
     186                }
     187
     188                // Append it to the list of GJD files
     189                if (!filename.empty()) {
     190                        _gjds.push_back(filename);
     191                }
     192
     193                // Read the next line
     194                line = indexfile.readLine();
     195        }
     196
     197        // Close the GJD index file
     198        indexfile.close();
     199}
     200
     201uint16 ResMan_v2::getRef(Common::String name, Common::String scriptname) {
     202        return 0;
     203}
     204
     205bool ResMan_v2::getResInfo(uint16 fileRef, ResInfo &resInfo) {
     206        // Open the RL file
     207        Common::File rlFile;
     208        if (!rlFile.open("dir.rl")) {
     209                error("Groovie::Resource: Couldn't open dir.rl");
     210                return false;
     211        }
     212
     213        // Seek to the position of the desired resource
     214        rlFile.seek(fileRef * 32);
     215        if (rlFile.eos()) {
     216                rlFile.close();
     217                error("Groovie::Resource: Invalid resource number: 0x%04X", fileRef);
     218                return false;
     219        }
     220
     221        // Read the resource information
     222        rlFile.readUint32LE(); // Unknown
     223        resInfo.offset = rlFile.readUint32LE();
     224        resInfo.size = rlFile.readUint32LE();
     225        resInfo.gjd = rlFile.readUint16LE();
     226
     227        // Read the resource name (just for debugging purposes)
     228        char resname[12];
     229        rlFile.read(resname, 12);
     230        debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
     231
     232        // 6 padding bytes? (it looks like they're always 0)
     233
     234        // Close the resource RL file
     235        rlFile.close();
     236
     237        return true;
     238}
     239
     240} // End of Groovie namespace
  • engines/groovie/jpeg.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_JPEG_H
     27#define GROOVIE_JPEG_H
     28
     29#include "common/stream.h"
     30
     31namespace Groovie {
     32
     33#define JPEG_MAX_QUANT_TABLES 4
     34#define JPEG_MAX_HUFF_TABLES 2
     35
     36class JPEG {
     37public:
     38        JPEG();
     39        ~JPEG();
     40
     41        bool read(Common::ReadStream *str);
     42
     43private:
     44        void reset();
     45
     46        Common::ReadStream *_str;
     47        uint16 _w, _h;
     48
     49        // Image components
     50        uint8 _numComp;
     51        struct Component {
     52                // Global values
     53                uint8 id;
     54                uint8 factorH;
     55                uint8 factorV;
     56                uint8 quantTableSelector;
     57
     58                // Scan specific values
     59                uint8 DCentropyTableSelector;
     60                uint8 ACentropyTableSelector;
     61                int8 DCpredictor;
     62        };
     63        Component *_components;
     64
     65        // Scan components
     66        uint8 _numScanComp;
     67        Component **_scanComp;
     68        Component *_currentComp;
     69
     70        // Maximum sampling factors, used to calculate the interleaving of the MCU
     71        uint8 _maxFactorV;
     72        uint8 _maxFactorH;
     73
     74        // Zig-Zag order
     75        static uint8 _zigZagOrder[64];
     76
     77        // Quantization tables
     78        uint8 *_quant[JPEG_MAX_QUANT_TABLES];
     79
     80        // Huffman tables
     81        struct HuffmanTable {
     82                uint8 count;
     83                uint8 *values;
     84                uint8 *sizes;
     85                uint16 *codes;
     86        };
     87        HuffmanTable _huff[2 * JPEG_MAX_HUFF_TABLES];
     88
     89        // Marker read functions
     90        bool readSOF0();
     91        bool readDHT();
     92        bool readSOS();
     93        bool readDQT();
     94
     95        // Helper functions
     96        bool readMCU(uint16 xMCU, uint16 yMCU);
     97        bool readDataUnit(uint16 x, uint16 y);
     98        int8 readDC();
     99        void readAC(int8 *out);
     100        int16 readSignedBits(uint8 numBits);
     101
     102        // Huffman decoding
     103        uint8 readHuff(uint8 table);
     104        uint8 readBit();
     105        uint8 _bitsData;
     106        uint8 _bitsNumber;
     107};
     108
     109} // End of Groovie namespace
     110
     111#endif // GROOVIE_JPEG_H
  • engines/groovie/debug.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_DEBUG_H
     27#define GROOVIE_DEBUG_H
     28
     29#include "gui/debugger.h"
     30#include "engines/engine.h"
     31
     32namespace Groovie {
     33
     34class Script;
     35class GroovieEngine;
     36
     37class Debugger : public GUI::Debugger {
     38public:
     39        Debugger(GroovieEngine *vm);
     40        ~Debugger();
     41
     42private:
     43        GroovieEngine *_vm;
     44        Script *_script;
     45        OSystem *_syst;
     46
     47        int getNumber(const char *arg);
     48
     49        bool cmd_step(int argc, const char **argv);
     50        bool cmd_go(int argc, const char **argv);
     51        bool cmd_pc(int argc, const char **argv);
     52        bool cmd_bg(int argc, const char **argv);
     53        bool cmd_fg(int argc, const char **argv);
     54        bool cmd_mem(int argc, const char **argv);
     55        bool cmd_loadgame(int argc, const char **argv);
     56        bool cmd_savegame(int argc, const char **argv);
     57        bool cmd_playref(int argc, const char **argv);
     58        bool cmd_dumppal(int argc, const char **argv);
     59};
     60
     61} // End of Groovie namespace
     62
     63#endif // GROOVIE_DEBUG_H
  • engines/groovie/lzss.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/lzss.h"
     27
     28#define OUT_BUFF_SIZE 131072
     29#define COMP_THRESH 3 // Compression not attempted if string to be compressed is less than 3 long
     30
     31LzssReadStream::LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits) {
     32        /*
     33        TODO: Nasty hack. Make a buffer bigger than I'll ever need... probably.
     34        What should *really* happen is I should define a whole new type of stream
     35        that gets lzss decompressed on the fly
     36        */
     37        _outLzssBufData = (uint8 *)malloc(OUT_BUFF_SIZE);
     38        _size = decodeLZSS(indata, lengthmask, lengthbits);
     39        _pos = 0;
     40}
     41
     42LzssReadStream::~LzssReadStream() {
     43        free(_outLzssBufData);
     44}
     45
     46uint32 LzssReadStream::decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits) {
     47        uint32 N = 1 << (16 - lengthbits); /* History buffer size */
     48        byte *histbuff = new byte[N]; /* History buffer */
     49        memset(histbuff, 0, N);
     50        uint32 outstreampos = 0;
     51        uint32 bufpos = 0;
     52
     53        while (!in->eos()) {
     54                byte flagbyte = in->readByte();
     55                for (uint32 i = 1; i <= 8; i++) {
     56                        if (!in->eos()) {
     57                                if ((flagbyte & 1) == 0) {
     58                                        uint32 offsetlen = in->readUint16LE();
     59                                        if (offsetlen == 0) {
     60                                                break;
     61                                        }
     62                                        uint32 length = (offsetlen & lengthmask) + COMP_THRESH;
     63                                        uint32 offset = (bufpos - (offsetlen >> lengthbits)) & (N - 1);
     64                                        for (uint32 j = 0; j < length; j++) {
     65                                                byte tempa = histbuff[(offset + j) & (N - 1)];
     66                                                _outLzssBufData[outstreampos++] = tempa;
     67                                                histbuff[bufpos] = tempa;
     68                                                bufpos = (bufpos + 1) & (N - 1);
     69                                        }
     70                                } else {
     71                                        byte tempa = in->readByte();
     72                                        if (in->eos()) {
     73                                                break;
     74                                        }
     75                                        _outLzssBufData[outstreampos++] = tempa;
     76                                        histbuff[bufpos] = tempa;
     77                                        bufpos = (bufpos + 1) & (N - 1);
     78                                }
     79                                flagbyte = flagbyte >> 1;
     80                        }
     81                }
     82        }
     83        delete[] histbuff;
     84        return outstreampos;
     85}
     86
     87bool LzssReadStream::eos() const {
     88        return _pos >= _size;
     89}
     90
     91uint32 LzssReadStream::read(void *buf, uint32 size) {
     92        if (size > _size - _pos)
     93                size = _size - _pos;
     94
     95        memcpy(buf, &_outLzssBufData[_pos], size);
     96        _pos += size;
     97
     98        return size;
     99}
  • engines/groovie/player.cpp

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#include "groovie/groovie.h"
     27#include "groovie/player.h"
     28
     29namespace Groovie {
     30
     31VideoPlayer::VideoPlayer(GroovieEngine *vm) :
     32        _vm(vm), _syst(vm->_system), _file(NULL), _audioStream(NULL) {
     33}
     34
     35bool VideoPlayer::load(Common::SeekableReadStream *file, uint16 flags) {
     36        _file = file;
     37        _flags = flags;
     38        _audioStream = NULL;
     39
     40        uint16 fps = loadInternal();
     41
     42        if (fps != 0) {
     43                _millisBetweenFrames = 1000 / fps;
     44                _begunPlaying = false;
     45                return true;
     46        } else {
     47                _file = NULL;
     48                return false;
     49        }
     50}
     51
     52bool VideoPlayer::playFrame() {
     53        bool end = true;
     54
     55        // Process the next frame while the file is open
     56        if (_file) {
     57                end = playFrameInternal();
     58        }
     59
     60        // The file has been completely processed
     61        if (end) {
     62                _file = NULL;
     63
     64                // Wait for pending audio
     65                if (_audioStream) {
     66                        if (_audioStream->endOfData()) {
     67                                // Mark the audio stream as finished (no more data will be appended)
     68                                _audioStream->finish();
     69                        } else {
     70                                // Don't end if there's still audio playing
     71                                end = false;
     72                        }
     73                }
     74        }
     75
     76        return end;
     77}
     78
     79void VideoPlayer::waitFrame() {
     80        uint32 currTime = _syst->getMillis();
     81        if (!_begunPlaying) {
     82                _begunPlaying = true;
     83                _lastFrameTime = currTime;
     84        } else {
     85                uint32 millisDiff = currTime - _lastFrameTime;
     86                if (millisDiff < _millisBetweenFrames) {
     87                        debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Delaying %d (currTime=%d, _lastFrameTime=%d, millisDiff=%d, _millisBetweenFrame=%d)",
     88                                        _millisBetweenFrames - millisDiff, currTime, _lastFrameTime, millisDiff, _millisBetweenFrames);
     89                        _syst->delayMillis(_millisBetweenFrames - millisDiff);
     90                        currTime = _syst->getMillis();
     91                        debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Finished delay at %d", currTime);
     92                }
     93                debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Frame displayed at %d (%f FPS)", currTime, 1000.0 / (currTime - _lastFrameTime));
     94                _lastFrameTime = currTime;
     95        }
     96}
     97
     98} // End of Groovie namespace
  • engines/groovie/resource.h

     
     1/* ScummVM - Graphic Adventure Engine
     2 *
     3 * ScummVM is the legal property of its developers, whose names
     4 * are too numerous to list here. Please refer to the COPYRIGHT
     5 * file distributed with this source distribution.
     6 *
     7 * This program is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU General Public License
     9 * as published by the Free Software Foundation; either version 2
     10 * of the License, or (at your option) any later version.
     11
     12 * This program is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 * GNU General Public License for more details.
     16
     17 * You should have received a copy of the GNU General Public License
     18 * along with this program; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 *
     21 * $URL$
     22 * $Id$
     23 *
     24 */
     25
     26#ifndef GROOVIE_RESOURCE_H
     27#define GROOVIE_RESOURCE_H
     28
     29namespace Groovie {
     30
     31struct ResInfo {
     32        uint16 gjd;
     33        uint32 offset;
     34        uint32 size;
     35};
     36
     37class ResMan {
     38public:
     39        virtual ~ResMan() {};
     40
     41        Common::SeekableReadStream *open(uint16 fileRef);
     42        virtual uint16 getRef(Common::String name, Common::String scriptname = "") = 0;
     43
     44protected:
     45        Common::Array<Common::String> _gjds;
     46        virtual bool getResInfo(uint16 fileRef, ResInfo &resInfo) = 0;
     47
     48        uint16 _lastGjd;
     49};
     50
     51class ResMan_t7g : public ResMan {
     52public:
     53        ResMan_t7g();
     54        ~ResMan_t7g() {};
     55
     56        uint16 getRef(Common::String name, Common::String scriptname);
     57        bool getResInfo(uint16 fileRef, ResInfo &resInfo);
     58};
     59
     60class ResMan_v2 : public ResMan {
     61public:
     62        ResMan_v2();
     63        ~ResMan_v2() {};
     64
     65        uint16 getRef(Common::String name, Common::String scriptname);
     66        bool getResInfo(uint16 fileRef, ResInfo &resInfo);
     67};
     68
     69} // End of Groovie namespace
     70
     71#endif // GROOVIE_RESOURCE_H
  • base/plugins.cpp

     
    108108                #if PLUGIN_ENABLED_STATIC(GOB)
    109109                LINK_PLUGIN(GOB)
    110110                #endif
     111                #if PLUGIN_ENABLED_STATIC(GROOVIE)
     112                LINK_PLUGIN(GROOVIE)
     113                #endif
    111114                #if PLUGIN_ENABLED_STATIC(IGOR)
    112115                LINK_PLUGIN(IGOR)
    113116                #endif