From 1f7ccd02938a1e418bf83ed44014631187df7fcb Mon Sep 17 00:00:00 2001 From: James Brown Date: Thu, 11 Nov 2004 13:37:35 +0000 Subject: Bring kyra up-to-date with patch 1051358 svn-id: r15786 --- kyra/codecs.cpp | 10 +- kyra/cpsimage.cpp | 64 +++++----- kyra/kyra.cpp | 133 +++++++++++++++++---- kyra/kyra.h | 21 +++- kyra/module.mk | 1 + kyra/palette.cpp | 4 +- kyra/resource.cpp | 92 ++++++++++----- kyra/resource.h | 2 +- kyra/script.cpp | 84 +++++++------ kyra/script.h | 9 +- kyra/script_v1.cpp | 3 +- kyra/sound.cpp | 167 ++++++++++++++++++++++++++ kyra/sound.h | 58 +++++++++ kyra/wsamovie.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++-- kyra/wsamovie.h | 69 +++++++++-- 15 files changed, 905 insertions(+), 151 deletions(-) create mode 100644 kyra/sound.cpp create mode 100644 kyra/sound.h diff --git a/kyra/codecs.cpp b/kyra/codecs.cpp index 1205442ec1..e90ebdcd68 100644 --- a/kyra/codecs.cpp +++ b/kyra/codecs.cpp @@ -84,9 +84,13 @@ int Compression::decode80(const uint8* image_in, uint8* image_out) { copyp = (const uint8*)&image_out[READ_LE_UINT16(readp)]; readp += 2; - memcpy(writep, copyp, count); - writep += count; - copyp += count; + // FIXME: Using memmove sometimes segfaults + // (reproducably for Ender), which suggests something Bad here + //memmove(writep, copyp, count); + //writep += count; + //copyp += count; + while (count--) + *writep++ = *copyp++; } else if (count == 0x3e) { //command 3 (11111110 c c v): fill diff --git a/kyra/cpsimage.cpp b/kyra/cpsimage.cpp index 20b72438fe..bb50c0a916 100644 --- a/kyra/cpsimage.cpp +++ b/kyra/cpsimage.cpp @@ -51,6 +51,7 @@ namespace Kyra { if (!buffer) { error("resource created without data"); } + _ownPalette = 0; Common::MemoryReadStream bufferstream(buffer, size); // reads in the Header @@ -61,11 +62,15 @@ namespace Kyra { // lets check a bit if(_cpsHeader._pal == 0x3000000) { - warning("CPS images with a palette aren't supported"); - - // skip 768 bytes // if this was a compressed palette you should have strange graphics - bufferstream.seek(bufferstream.pos() + 768); + + uint8* palbuffer = new uint8[768]; + assert(palbuffer); + + bufferstream.read(palbuffer, 768 * sizeof(uint8)); + + _ownPalette = new Palette(palbuffer, 768); + assert(palbuffer); } _image = new uint8[_cpsHeader._imagesize]; @@ -104,16 +109,15 @@ namespace Kyra { } void CPSImage::drawToPlane(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 x, uint16 y) { + uint8* src = _image; + uint8* dst = &plane[y * planepitch + x]; + uint32 copysize = planepitch - x; + + if (copysize > _width) + copysize = _width; + if (_transparency == -1) { // 'fast' blitting - - uint8* src = _image; - uint8* dst = &plane[y * planepitch + x]; - uint32 copysize = planepitch - x; - - if (copysize > _width) - copysize = _width; - for (uint16 y_ = 0; y_ < _height && y + y_ < planeheight; ++y_) { memcpy(dst, src, copysize * sizeof(uint8)); dst += planepitch; @@ -122,11 +126,9 @@ namespace Kyra { } else { // oh no! we have transparency so we have a very slow copy :/ - uint8* src = _image; - uint8* dst = &plane[y * planepitch + x]; for (uint16 yadd = 0; yadd < _height; ++yadd) { - for (uint16 xadd = 0; xadd < _width; ++xadd) { + for (uint16 xadd = 0; xadd < copysize; ++xadd) { if (*src == _transparency) { ++dst; ++src; @@ -135,23 +137,26 @@ namespace Kyra { } } - dst += planepitch - _width; + src += _width - copysize; + dst += planepitch - copysize; } } } void CPSImage::drawToPlane(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 x, uint16 y, uint16 srcx, uint16 srcy, uint16 srcwidth, uint16 srcheight) { + uint8* src = &_image[srcy * _width + srcx]; + uint8* dst = &plane[y * planepitch + x]; + uint32 copysize = planepitch - x; + + if (srcwidth > _width) + srcwidth = _width; + + if (copysize > srcwidth) + copysize = srcwidth; + if (_transparency == -1) { // 'fast' blitting - - uint8* src = &_image[srcy * _width + srcx]; - uint8* dst = &plane[y * planepitch + x]; - uint32 copysize = planepitch - x; - - if (copysize > srcwidth) - copysize = srcwidth; - for (uint16 y_ = 0; y_ < srcheight && y + y_ < planeheight; ++y_) { memcpy(dst, src, copysize * sizeof(uint8)); dst += planepitch; @@ -160,12 +165,9 @@ namespace Kyra { } else { // oh no! we have transparency so we have a very slow copy :/ - // blit it without transparency - uint8* src = &_image[srcy * _width + srcx]; - uint8* dst = &plane[y * planepitch + x]; - for (uint16 yadd = 0; yadd < _height; ++yadd) { - for (uint16 xadd = 0; xadd < _width; ++xadd) { + for (uint16 yadd = 0; yadd < srcheight; ++yadd) { + for (uint16 xadd = 0; xadd < copysize; ++xadd) { if (*src == _transparency) { ++dst; ++src; @@ -174,8 +176,8 @@ namespace Kyra { } } - dst += planepitch - _width; - src += _width - srcwidth; + dst += planepitch - copysize; + src += _width - copysize; } } } diff --git a/kyra/kyra.cpp b/kyra/kyra.cpp index c5248e5a00..9247c84992 100644 --- a/kyra/kyra.cpp +++ b/kyra/kyra.cpp @@ -28,10 +28,13 @@ #include "sound/mixer.h" #include "common/file.h" #include "common/config-manager.h" +#include "sound/mididrv.h" #include "kyra.h" #include "resource.h" #include "script.h" +#include "wsamovie.h" +#include "sound.h" struct KyraGameSettings { const char *name; @@ -44,10 +47,13 @@ struct KyraGameSettings { } }; -static const KyraGameSettings kyra_settings[] = { - {"kyra1cd", "Legend of Kyrandia (CD)", GF_TALKIE & GF_KYRA1, "CHAPTER1.VRM"}, - {"kyra1", "Legend of Kyrandia (Floppy)", GF_FLOPPY & GF_KYRA1, "INTRO.SND"}, - { 0, 0, 0, 0} +static const KyraGameSettings kyra_settings[] = { + {"kyra1", "Legend of Kyrandia (Floppy)", GF_FLOPPY | GF_KYRA1, "INTRO.SND"}, + {"kyra1cd", "Legend of Kyrandia (CD)", GF_TALKIE | GF_KYRA1, "CHAPTER1.VRM"}, + {"kyra2", "Hand of Fate (Floppy)", GF_FLOPPY | GF_KYRA2, 0 }, + {"kyra2cd", "Hand of Fate (CD)", GF_TALKIE | GF_KYRA2, "AUDIO.PAK"}, + {"kyra3", "Malcom's Revenge", GF_TALKIE | GF_KYRA3, "K3INTRO0.VQA"}, + {0, 0, 0, 0} }; GameList Engine_KYRA_gameList() { @@ -99,10 +105,40 @@ KyraEngine::KyraEngine(GameDetector *detector, OSystem *syst) _mixer->setVolume(ConfMan.getInt("sfx_volume") * ConfMan.getInt("master_volume") / 255); + // gets the game + if (detector->_game.features & GF_KYRA1) { + if (detector->_game.features & GF_FLOPPY) + _game = KYRA1; + else + _game = KYRA1CD; + } else if (detector->_game.features & GF_KYRA2) { + if (detector->_game.features & GF_FLOPPY) + _game = KYRA2; + else + _game = KYRA2CD; + } else if (detector->_game.features & GF_KYRA3) { + _game = KYRA3; + } else { + error("unknown game"); + } + + MidiDriver *driver = GameDetector::createMidi(GameDetector::detectMusicDriver(MDT_NATIVE | MDT_PREFER_NATIVE)); + if (driver) { + if (ConfMan.getBool("native_mt32")) + driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + _midiDriver = new MusicPlayer(driver, this); + assert(_midiDriver); + _midiDriver->hasNativeMT32(ConfMan.getBool("native_mt32")); + _midiDriver->setVolume(255); + } else { + warning("Couldn't create MIDI driver... No music!"); + _midiDriver = NULL; + }; + // Initialize backen syst->initSize(320, 200); - _screen = new uint8[320 * 200]; - memset(_screen, 0, 320 * 200); + _screen = new uint8[320*200]; + memset(_screen, 0, sizeof(uint8) * 320 * 200); _resMgr = new Resourcemanager(this); assert(_resMgr); @@ -110,15 +146,23 @@ KyraEngine::KyraEngine(GameDetector *detector, OSystem *syst) setCurrentPalette(_resMgr->loadPalette("PALETTE.COL")); // loads the 2 cursors - _mouse = _resMgr->loadImage("MOUSE.CPS"); //startup.pak + _mouse = _resMgr->loadImage("MOUSE.CPS"); _items = _resMgr->loadImage("ITEMS.CPS"); // loads the Font _font = _resMgr->loadFont("8FAT.FNT"); - - // loads out scripts + _npcScript = _resMgr->loadScript("_NPC.EMC"); - _currentScript = _resMgr->loadScript("_STARTUP.EMC"); + + // loads the scripts (only Kyrandia 1) + if (_game == KYRA1 || _game == KYRA1CD) { + _currentScript = _resMgr->loadScript("_STARTUP.EMC"); + } else { + error("game start files not known"); + } + + assert(_npcScript); + assert(_currentScript); } KyraEngine::~KyraEngine() { @@ -138,7 +182,7 @@ void KyraEngine::errorString(const char *buf1, char *buf2) { void KyraEngine::go() { warning("Kyrandia Engine ::go()"); // starts the init script - if (!_currentScript->startScript(kSetupScene)) { + /*if (!_currentScript->startScript(kSetupScene)) { error("couldn't init '_STARTUP.EMC' script"); } @@ -148,25 +192,67 @@ void KyraEngine::go() { } else { warning("init script returned: %d", _currentScript->state()); } + }*/ + + Movie* movie = _resMgr->loadMovie("MAL-KAL.WSA"); + assert(movie); + CPSImage* image = _resMgr->loadImage("GEMCUT.CPS"); + assert(image); + + int16 currentFrame = 0; + uint32 lastFrameChange = 0; + + image->transparency(0); + image->drawToPlane(_screen, 320, 200, 0, 0, 0, 0, 320, 136); + movie->setImageBackground(_screen, 320, 200); + movie->position(16, 58); + + setCurrentPalette(_resMgr->loadPalette("MAL-KAL.COL")); + + uint8* _buffer = new uint8[320 * 200]; + assert(_buffer); + memcpy(_buffer, _screen, 320 * 200); + movie->renderFrame(_buffer, 320, 200, movie->countFrames() - 1); + + if (_midiDriver) { + _midiDriver->playMusic("KYRA2A.XMI"); + _midiDriver->playTrack(3); } - + while(true) { OSystem::Event event; //if (_debugger->isAttached()) // _debugger->onFrame(); - + + memcpy(_screen, _buffer, 320 * 200); + if (lastFrameChange + movie->frameChange() < _system->getMillis()) { + lastFrameChange = _system->getMillis(); + ++currentFrame; + + if (currentFrame >= (int16)movie->countFrames()) { + currentFrame = 0; + } + } + + movie->renderFrame(_screen, 320, 200, currentFrame); + _font->drawStringToPlane("This is only a test!", _screen, 320, 200, 75, 179, 136); + _font->drawStringToPlane("Nothing scripted!", _screen, 320, 200, 85, 189, 136); updateScreen(); while (g_system->pollEvent(event)) { switch (event.event_code) { - case OSystem::EVENT_QUIT: - g_system->quit(); - break; - default: - break; + case OSystem::EVENT_QUIT: + g_system->quit(); + break; + default: + break; } } _system->delayMillis(10); } + + delete movie; + delete image; + delete [] _buffer; } void KyraEngine::shutdown() { @@ -179,12 +265,15 @@ void KyraEngine::updateScreen(void) { } void KyraEngine::setCurrentPalette(Palette* pal, bool delNextTime) { -// if (_delPalNextTime) -// delete _currentPal; + if (!pal) + return; + +// if (_delPalNextTime) +// delete _currentPal; -// _delPalNextTime = delNextTime; +// _delPalNextTime = delNextTime; -// _currentPal = pal; +// _currentPal = pal; if (pal->getData()) { _system->setPalette(pal->getData(), 0, 256); diff --git a/kyra/kyra.h b/kyra/kyra.h index 2a6b5b1556..b0dfc34501 100644 --- a/kyra/kyra.h +++ b/kyra/kyra.h @@ -8,7 +8,7 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License @@ -31,7 +31,17 @@ enum { GF_FLOPPY = 1 << 0, GF_TALKIE = 1 << 1, GF_KYRA1 = 1 << 2, - GF_KYRA2 = 1 << 3 + GF_KYRA2 = 1 << 3, + GF_KYRA3 = 1 << 4, + GF_AUDIOCD = 1 << 5 // FM-Towns versions seems to use audio CD +}; + +enum { + KYRA1 = 0, + KYRA1CD = 1, + KYRA2 = 2, + KYRA2CD = 3, + KYRA3 = 4 }; namespace Kyra { @@ -40,6 +50,7 @@ namespace Kyra { class Font; class Palette; class VMContext; + class MusicPlayer; class KyraEngine : public Engine { public: @@ -51,13 +62,17 @@ public: void setCurrentPalette(Palette* pal, bool delNextTime = true); Resourcemanager* resManager(void) { return _resMgr; } -// MidiDriver* midiDriver(void) { return _midiDriver; } + MusicPlayer* midiDriver(void) { return _midiDriver; } + + uint8 game(void) { return _game; } protected: void go(); void shutdown(); Resourcemanager* _resMgr; + MusicPlayer* _midiDriver; uint8 *_screen; + uint8 _game; Font* _font; CPSImage* _mouse; diff --git a/kyra/module.mk b/kyra/module.mk index 57837decb4..1026f29468 100644 --- a/kyra/module.mk +++ b/kyra/module.mk @@ -9,6 +9,7 @@ MODULE_OBJS := \ kyra/resource.o \ kyra/script_v1.o \ kyra/script.o \ + kyra/sound.o \ kyra/wsamovie.o MODULE_DIRS += \ diff --git a/kyra/palette.cpp b/kyra/palette.cpp index ee7826d127..8087672e6e 100644 --- a/kyra/palette.cpp +++ b/kyra/palette.cpp @@ -21,7 +21,7 @@ #include "stdafx.h" #include "resource.h" - + #include "common/stream.h" #include "codecs.h" @@ -52,7 +52,7 @@ namespace Kyra { } delete [] data; - data = _palette; + data = _palette; } // hmm.. common/system.h Docu is wrong or SDL Backend has a bug :) diff --git a/kyra/resource.cpp b/kyra/resource.cpp index 2a91654d38..37b563f457 100644 --- a/kyra/resource.cpp +++ b/kyra/resource.cpp @@ -34,21 +34,48 @@ namespace Kyra { // ugly a hardcoded list // TODO: use the FS Backend to get all .PAK Files and load them - static const char* kyraFilelist[] = { + // or any other thing to get all files + static const char* kyra1Filelist[] = { "A_E.PAK", "DAT.PAK", "F_L.PAK", "MAP_5.PAK", "MSC.PAK", "M_S.PAK", "S_Z.PAK", "WSA1.PAK", "WSA2.PAK", "WSA3.PAK", "WSA4.PAK", "WSA5.PAK", - "WSA6.PAK", "startup.pak", "intro1.pak", 0 + "WSA6.PAK", 0 }; - for (uint32 tmp = 0; kyraFilelist[tmp]; ++tmp) { + static const char* kyra1CDFilelist[] = { + "ADL.PAK", "BRINS.PAK", "CLIFF.PAK", "ENTER.PAK", "FORESTA.PAK", "GEM.PAK", "INTRO1.PAK", + "LEPHOLE.PAK", "OAKS.PAK", "SPELL.PAK", "WILLOW.PAK", "ALCHEMY.PAK", "BROKEN.PAK", "COL.PAK", + "EXTHEAL.PAK", "FORESTB.PAK", "GEMCUT.PAK", "INTRO2.PAK", "LIBRARY.PAK", "PLATEAU.PAK", "SPRING.PAK", + "WISE.PAK", "ALGAE.PAK", "BURN.PAK", "DARMS.PAK", "EXTPOT.PAK", "FORESTC.PAK", "GENCAVB.PAK", + "INTRO3.PAK", "MISC.PAK", "PLTCAVE.PAK", "SQUARE.PAK", "XEDGE.PAK", "ALTAR.PAK", "CASTLE.PAK", + "DEAD.PAK", "EXTSPEL.PAK", "FOUNTN.PAK", "GENHALL.PAK", "INTRO4.PAK", "MIX.PAK", "POTION.PAK", + "STARTUP.PAK", "XEDGEB.PAK", "ARCH.PAK", "CATACOM.PAK", "DNSTAIR.PAK", "FALLS.PAK", "FOYER.PAK", + "GEN_CAV.PAK", "KITCHEN.PAK", "MOONCAV.PAK", "RUBY.PAK", "STUMP.PAK", "XEDGEC.PAK", "BALCONY.PAK", + "CAVE.PAK", "DRAGON.PAK", "FESTSTH.PAK", "FSOUTH.PAK", "GLADE.PAK", "KYRAGEM.PAK", "NCLIFF.PAK", + "SICKWIL.PAK", "TEMPLE.PAK", "XMI.PAK", "BELROOM.PAK", "CAVEB.PAK", "EDGE.PAK", "FGOWEST.PAK", + "FSOUTHB.PAK", "GRAVE.PAK", "LAGOON.PAK", "NCLIFFB.PAK", "SND.PAK", "TRUNK.PAK", "ZROCK.PAK", + "BONKBG.PAK", "CGATE.PAK", "EDGEB.PAK", "FINALE.PAK", "FWSTSTH.PAK", "GRTHALL.PAK", "LANDING.PAK", + "NWCLIFB.PAK", "SONG.PAK", "UPSTAIR.PAK", "BRIDGE.PAK", "CHASM.PAK", "EMCAV.PAK", "FNORTH.PAK", + "GATECV.PAK", "HEALER.PAK", "LAVA.PAK", "NWCLIFF.PAK", "SORROW.PAK", "WELL.PAK", 0 + }; + + const char** usedFilelist = 0; + + if (_engine->game() == KYRA1) + usedFilelist = kyra1Filelist; + else if (_engine->game() == KYRA1CD) + usedFilelist = kyra1CDFilelist; + else + error("no filelist found for this game"); + + for (uint32 tmp = 0; usedFilelist[tmp]; ++tmp) { // prefetch file - PAKFile* file = new PAKFile(kyraFilelist[tmp]); + PAKFile* file = new PAKFile(usedFilelist[tmp]); assert(file); - if (file->isOpen() && file->isValid()) + if (file->isOpen() && file->isValid()) _pakfiles.push_back(file); else - warning("couldn't load file '%s' correctly", kyraFilelist[tmp]); + debug("couldn't load file '%s' correctly", usedFilelist[tmp]); } } @@ -63,11 +90,8 @@ namespace Kyra { uint8* Resourcemanager::fileData(const char* file, uint32* size) { uint8* buffer = 0; - - debug("looking for file '%s'", file); - File file_; - + // test to open it in the main dir if (file_.open(file)) { @@ -83,37 +107,39 @@ namespace Kyra { } else { // opens the file in a PAK File Common::List::iterator start = _pakfiles.begin(); - + for (;start != _pakfiles.end(); ++start) { *size = (*start)->getFileSize(file); - - if (!*size) + + if (!(*size)) continue; - + buffer = new uint8[*size]; assert(buffer); - + // creates a copy of the file memcpy(buffer, (*start)->getFile(file), *size); - + break; } } if (!buffer || !(*size)) { - warning("couldn't find file '%s'", file); + return 0; } return buffer; } - Palette* Resourcemanager::loadPalette(const char* file) { + Palette* Resourcemanager::loadPalette(const char* file) { uint32 size = 0; uint8* buffer = 0; buffer = fileData(file, &size); - if (!buffer) + if (!buffer) { + warning("ResMgr: Failed loading palette %s", file); return 0; + } return new Palette(buffer, size); } @@ -140,9 +166,12 @@ namespace Kyra { uint32 size = 0; uint8* buffer = 0; buffer = fileData(file, &size); - if (!buffer) + if (!buffer || !size) return 0; - return new WSAMovieV1(buffer, size); + if (_engine->game() == KYRA1 || _engine->game() == KYRA1CD) + return new WSAMovieV1(buffer, size, _engine->game()); + else + return new WSAMovieV2(buffer, size); } VMContext* Resourcemanager::loadScript(const char* file) { @@ -154,13 +183,13 @@ namespace Kyra { /////////////////////////////////////////// // Pak file manager #define PAKFile_Iterate Common::List::iterator start=_files.begin();start != _files.end(); ++start - PAKFile::PAKFile(const Common::String& file) { + PAKFile::PAKFile(/*const Common::String &path, */const Common::String& file) { File pakfile; _buffer = 0; _open = false; - if (!pakfile.open(file.c_str())) { - warning("PAKFile couldn't open: '%s'", file.c_str()); + if (!pakfile.open(file.c_str())){ /*, File::kFileReadMode, path.c_str())) {*/ + printf("pakfile couldn't open %s\n", file.c_str()); return; } @@ -184,18 +213,25 @@ namespace Kyra { // saves the name chunk->_name = reinterpret_cast(_buffer + pos); pos += strlen(chunk->_name) + 1; - if(!chunk->_name) + if(!(*chunk->_name)) break; endoffset = READ_LE_UINT32(_buffer + pos); pos += 4; + + if (endoffset == 0) { + endoffset = filesize; + } chunk->_data = _buffer + startoffset; chunk->_size = endoffset - startoffset; - startoffset = endoffset; - _files.push_back(chunk); + + if (endoffset == filesize) + break; + + startoffset = endoffset; } _open = true; } @@ -212,7 +248,7 @@ namespace Kyra { } const uint8* PAKFile::getFile(const char* file) { - for (PAKFile_Iterate) { + for (PAKFile_Iterate) { if (!scumm_stricmp((*start)->_name, file)) return (*start)->_data; } diff --git a/kyra/resource.h b/kyra/resource.h index 4aadd83b5a..9b159c427d 100644 --- a/kyra/resource.h +++ b/kyra/resource.h @@ -114,7 +114,7 @@ namespace Kyra { bool hasPalette(void) { return (_ownPalette != 0); } // if col == -1 then no transparany - void setTransparencyColor(int16 col) { _transparency = col; } + void transparency(int16 col) { _transparency = col; } void drawToPlane(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 x, uint16 y); void drawToPlane(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 x, uint16 y, diff --git a/kyra/script.cpp b/kyra/script.cpp index 8f13eb44b6..bb58d2c887 100644 --- a/kyra/script.cpp +++ b/kyra/script.cpp @@ -25,6 +25,7 @@ #include "resource.h" #include "common/stream.h" +#include "common/util.h" #define COMMAND(x) { &VMContext::x, #x } #define OPCODE(x) { &VMContext::x, #x } @@ -54,7 +55,7 @@ namespace Kyra { // 0x0C COMMAND(c1_addToSP), COMMAND(c1_subFromSP), - COMMAND(c1_execOpcode), + COMMAND(c1_execOpcode), COMMAND(c1_ifNotGoTo), // 0x10 COMMAND(c1_negate), @@ -82,7 +83,7 @@ namespace Kyra { // 0x0C COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x10 COMMAND(o1_unknownOpcode), @@ -107,7 +108,7 @@ namespace Kyra { // 0x20 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x24 COMMAND(o1_unknownOpcode), @@ -132,7 +133,7 @@ namespace Kyra { // 0x34 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x38 COMMAND(o1_unknownOpcode), @@ -157,7 +158,7 @@ namespace Kyra { // 0x48 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x4C COMMAND(o1_unknownOpcode), @@ -182,7 +183,7 @@ namespace Kyra { // 0x5C COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x60 COMMAND(o1_unknownOpcode), @@ -207,7 +208,7 @@ namespace Kyra { // 0x70 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x74 COMMAND(o1_unknownOpcode), @@ -232,7 +233,7 @@ namespace Kyra { // 0x84 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0x88 COMMAND(o1_unknownOpcode), @@ -262,7 +263,7 @@ namespace Kyra { // 0x9C COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0xA0 COMMAND(o1_unknownOpcode), @@ -287,7 +288,7 @@ namespace Kyra { // 0xB0 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0xB4 COMMAND(o1_unknownOpcode), @@ -312,7 +313,7 @@ namespace Kyra { // 0xC4 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0xC8 COMMAND(o1_unknownOpcode), @@ -337,7 +338,7 @@ namespace Kyra { // 0xD8 COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0xDC COMMAND(o1_unknownOpcode), @@ -362,7 +363,7 @@ namespace Kyra { // 0xEC COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), - COMMAND(o1_unknownOpcode), + COMMAND(o1_unknownOpcode), COMMAND(o1_unknownOpcode), // 0xF0 COMMAND(o1_unknownOpcode), @@ -398,8 +399,8 @@ namespace Kyra { _scriptFileSize = 0; } - debug("--------------"); - + memset(_stack, 0, sizeof(int32) * ARRAYSIZE(_stack)); + // loads the new file _scriptFile = _engine->resManager()->fileData(file, &_scriptFileSize); @@ -415,32 +416,27 @@ namespace Kyra { while(true) { if (script.eof()) { break; - } + } // lets read only the first 4 chars script.read(chunkName, sizeof(uint8) * 4); chunkName[4] = '\0'; - debug("chunk name(4 chars): '%s'", chunkName); - + // check name of chunk if (!scumm_stricmp((const char *)chunkName, "FORM")) { // FreeKyra swaps the size I only read it in BigEndian :) _chunks[kForm]._size = script.readUint32BE(); - debug("_chunks[kForm]._size = %d", _chunks[kForm]._size); + debug("_chunks[kForm]._size = %d", _chunks[kForm]._size); } else if (!scumm_stricmp((const char *)chunkName, "TEXT")) { uint32 text_size = script.readUint32BE(); text_size += text_size % 2 != 0 ? 1 : 0; _chunks[kText]._data = _scriptFile + script.pos(); _chunks[kText]._size = READ_BE_UINT16(_chunks[kText]._data) >> 1; - _chunks[kText]._additional = _chunks[kText]._data + (_chunks[kText]._size << 1); - debug("_chunks[kText]._size = %d, real chunk size = %d", _chunks[kText]._size, text_size); - + _chunks[kText]._additional = _chunks[kText]._data + (_chunks[kText]._size << 1); script.seek(script.pos() + text_size); } else if (!scumm_stricmp((const char *)chunkName, "DATA")) { _chunks[kData]._size = script.readUint32BE(); - _chunks[kData]._data = _scriptFile + script.pos(); - debug("_chunks[kData]._size = %d", _chunks[kData]._size); - + _chunks[kData]._data = _scriptFile + script.pos(); // mostly it will be the end of the file because all files should end with a 'DATA' chunk script.seek(script.pos() + _chunks[kData]._size); } else { @@ -451,25 +447,20 @@ namespace Kyra { if (!scumm_stricmp((const char *)chunkName, "EMC2ORDR")) { _chunks[kEmc2Ordr]._size = script.readUint32BE() >> 1; - _chunks[kEmc2Ordr]._data = _scriptFile + script.pos(); - debug("_chunks[kEmc2Ordr]._size = %d, real chunk size = %d", _chunks[kEmc2Ordr]._size, _chunks[kEmc2Ordr]._size * 2); - + _chunks[kEmc2Ordr]._data = _scriptFile + script.pos(); script.seek(script.pos() + _chunks[kEmc2Ordr]._size * 2); } else { // any unkown chunk or problems with seeking through the file - error("unknown chunk"); + error("unknown chunk(%s)", chunkName); } } } - - // so file loaded - debug("--------------"); } int32 VMContext::param(int32 index) { - if (_stackPos - index + 1 >= 16 || _stackPos - index + 1 < 0) + if (_stackPos - index - 1 >= ARRAYSIZE(_stack) || _stackPos - index - 1 < 0) return -0xFFFF; - return _stack[_stackPos - index + 1]; + return _stack[_stackPos - index - 1]; } const char* VMContext::stringAtIndex(int32 index) { @@ -485,11 +476,28 @@ namespace Kyra { return false; } - _instructionPos = (READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[func]) << 1) + 2; + _instructionPos = READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[func]) << 1; _stackPos = 0; _tempPos = 0; _delay = 0; _scriptState = kScriptRunning; + + uint32 pos = 0xFFFFFFFE; + + // get start of next script + for (uint32 tmp = 0; tmp < _chunks[kEmc2Ordr]._size; ++tmp) { + if ((uint32)((READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[tmp]) << 1)) > (uint32)_instructionPos && + (uint32)((READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[tmp]) << 1)) < pos) { + pos = ((READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[tmp]) << 1)); + } + } + + if (pos > _scriptFileSize) { + pos = _scriptFileSize; + } + + _nextScriptPos = pos; + return true; } @@ -505,6 +513,9 @@ namespace Kyra { debug("_instructionPos( = %d) > _chunks[kData]._size( = %d)", _instructionPos, _chunks[kData]._size); _error = true; break; + } else if(_instructionPos >= _nextScriptPos) { + _scriptState = kScriptStopped; + break; } _currentCommand = *(script_start + _instructionPos++); @@ -525,9 +536,12 @@ namespace Kyra { _instructionPos += 2; } else { debug("unknown way of getting the command"); + // next thing + continue; } _currentCommand &= 0x1f; + if (_currentCommand < _numCommands) { CommandProc currentProc = _commands[_currentCommand].proc; (this->*currentProc)(); diff --git a/kyra/script.h b/kyra/script.h index ea04d87416..664850b62c 100644 --- a/kyra/script.h +++ b/kyra/script.h @@ -71,14 +71,15 @@ namespace Kyra { uint32 _delay; int32 _registers[32]; // registers of the interpreter - int32 _stack[32]; // our stack + int32 _stack[64]; // our stack // TODO: check for 'under'flow - int32 popStack(void) { return _stack[_stackPos--]; } + int32 popStack(void) { return _stack[--_stackPos]; } int32& topStack(void) { return _stack[_stackPos]; } uint32 _returnValue; + int32 _nextScriptPos; int32 _instructionPos; int32 _stackPos; int32 _tempPos; @@ -131,7 +132,7 @@ namespace Kyra { void c1_goToLine(void); // 0x00 void c1_setReturn(void); // 0x01 void c1_pushRetRec(void); // 0x02 - void c1_push(void); // 0x03 & 0x04 + void c1_push(void); // 0x03 & 0x04 void c1_pushVar(void); // 0x05 void c1_pushFrameNeg(void); // 0x06 void c1_pushFramePos(void); // 0x07 @@ -148,7 +149,7 @@ namespace Kyra { void c1_unknownCommand(void); // the opcode procs - void o1_0x68(void); // 0x68 + void o1_0x68(void); // 0x68 void o1_unknownOpcode(void); }; } // end of namespace Kyra diff --git a/kyra/script_v1.cpp b/kyra/script_v1.cpp index ec9030d628..c69c58c514 100644 --- a/kyra/script_v1.cpp +++ b/kyra/script_v1.cpp @@ -237,6 +237,7 @@ namespace Kyra { } void VMContext::o1_0x68(void) { - debug("o1_0x68 was called with param0: '%d' and param1: '%d'", param(0), param(1)); + debug("o1_0x68 was called with param0: '%d'", param(0)); + _error = true; } } // end of namespace Kyra diff --git a/kyra/sound.cpp b/kyra/sound.cpp new file mode 100644 index 0000000000..d631f35b5a --- /dev/null +++ b/kyra/sound.cpp @@ -0,0 +1,167 @@ +#include "sound.h" +#include "resource.h" + +// Instrument mapping for MT32 tracks emulated under GM. +static const byte mt32_to_gm[128] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117 // 7x +}; + +namespace Kyra { + MusicPlayer::MusicPlayer(MidiDriver* driver, KyraEngine* engine) { + _engine = engine; + _driver = driver; + _isPlaying = _nativeMT32 = false; + + memset(_channel, 0, sizeof(MidiChannel*) * 16); + memset(_channelVolume, 255, sizeof(uint8) * 16); + _volume = 0; + + int ret = open(); + if (ret != MERR_ALREADY_OPEN && ret != 0) { + error("couldn't open midi driver"); + } + } + + MusicPlayer::~MusicPlayer() { + _driver->setTimerCallback(NULL, NULL); + close(); + } + + void MusicPlayer::setVolume(int volume) { + if (volume < 0) + volume = 0; + else if (volume > 255) + volume = 255; + + if (_volume == volume) + return; + + _volume = volume; + + for (int i = 0; i < 16; ++i) { + if (_channel[i]) { + _channel[i]->volume(_channelVolume[i] * _volume / 255); + } + } + } + + int MusicPlayer::open() { + // Don't ever call open without first setting the output driver! + if (!_driver) + return 255; + + int ret = _driver->open(); + if (ret) + return ret; + + _driver->setTimerCallback(this, &onTimer); + return 0; + } + + void MusicPlayer::close() { + if (_driver) + _driver->close(); + _driver = 0; + } + + void MusicPlayer::send(uint32 b) { + uint8 channel = (byte)(b & 0x0F); + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume + uint8 volume = (uint8)((b >> 16) & 0x7F); + _channelVolume[channel] = volume; + volume = volume * _volume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) { + b = (b & 0xFFFF00FF) | mt32_to_gm[(b >> 8) & 0xFF] << 8; + } else if ((b & 0xFFF0) == 0x007BB0) { + //Only respond to All Notes Off if this channel + //has currently been allocated + if (!_channel[channel]) + return; + } + + if (!_channel[channel]) + _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + + if (_channel[channel]) + _channel[channel]->send(b); + } + + void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { + switch (type) { + case 0x2F: // End of Track + _parser->jumpToTick(0); + break; + default: + warning("Unhandled meta event: 0x%02x", type); + break; + } + } + + void MusicPlayer::playMusic(const char* file) { + uint32 size; + uint8* data = 0; + + data = (_engine->resManager())->fileData(file, &size); + + if (!data) { + warning("couldn't load '%s'", file); + return; + } + + playMusic(data, size); + } + + void MusicPlayer::playMusic(uint8* data, uint32 size) { + if (_isPlaying) + stopMusic(); + + _parser = MidiParser::createParser_XMIDI(); + assert(_parser); + + if (!_parser->loadMusic(data, size)) { + warning("Error reading track!"); + delete _parser; + _parser = 0; + return; + } + + _parser->setTrack(0); + _parser->setMidiDriver(this); + _parser->setTimerRate(getBaseTempo()); + + _isPlaying = true; + } + + void MusicPlayer::stopMusic() { + _isPlaying = false; + if (_parser) { + _parser->unloadMusic(); + delete _parser; + _parser = NULL; + } + } + + void MusicPlayer::onTimer(void *refCon) { + MusicPlayer *music = (MusicPlayer *)refCon; + if (music->_isPlaying) + music->_parser->onTimer(); + } + + void MusicPlayer::playTrack(uint8 track) { + if (_parser) { + _isPlaying = true; + _parser->setTrack(track); + _parser->jumpToTick(0); + } + } +} // end of namespace Kyra diff --git a/kyra/sound.h b/kyra/sound.h new file mode 100644 index 0000000000..dfe393e53c --- /dev/null +++ b/kyra/sound.h @@ -0,0 +1,58 @@ +#ifndef SOUND_H +#define SOUND_H + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "sound/mididrv.h" +#include "sound/midiparser.h" +#include "kyra.h" + +namespace Kyra { + class MusicPlayer : public MidiDriver { + + public: + + MusicPlayer(MidiDriver* driver, KyraEngine* engine); + ~MusicPlayer(); + + void setVolume(int volume); + int getVolume() { return _volume; } + + void hasNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; } + + void playMusic(const char* file); + void playMusic(uint8* data, uint32 size); + void stopMusic(); + + void playTrack(uint8 track); + + //MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); + + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } + + //Channel allocation functions + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + protected: + + static void onTimer(void *data); + + MidiChannel* _channel[16]; + uint8 _channelVolume[16]; + MidiDriver* _driver; + bool _nativeMT32; + uint8 _volume; + bool _isPlaying; + MidiParser* _parser; + KyraEngine* _engine; + + }; +} // end of namespace Kyra + +#endif diff --git a/kyra/wsamovie.cpp b/kyra/wsamovie.cpp index 056ece2770..1408482cf5 100644 --- a/kyra/wsamovie.cpp +++ b/kyra/wsamovie.cpp @@ -18,25 +18,67 @@ * $Header$ * */ - + #include "stdafx.h" #include "wsamovie.h" #include "codecs.h" #include "common/stream.h" +#ifdef DUMP_FILES +#include +#endif + namespace Kyra { - WSAMovieV1::WSAMovieV1(uint8* data, uint32 size) { + WSAMovieV1::WSAMovieV1(uint8* data, uint32 size, uint8 gameid) { if (!data) { error("resource created without data"); } + _background = 0; + _currentFrame = 0; + _ownPalette = 0; + _offsetTable = 0; + _prefetchedFrame = 0xFFFE; _buffer = data; // I like these Streams .... =) Common::MemoryReadStream datastream(data, size); datastream.read(&_wsaHeader, sizeof(_wsaHeader)); + +#ifdef DUMP_FILES + FILE* wsaheader = fopen("dumps/wsaheader.txt", "w+"); + + if (wsaheader) { + for (uint32 pos = 0; pos < sizeof(_wsaHeader); ++pos) + fprintf(wsaheader, "%d pos. byte: %d\n", pos + 1, ((uint8*)&_wsaHeader)[pos]); + fprintf(wsaheader, "\n"); + for (uint32 pos = 0; pos < sizeof(_wsaHeader) / 2; ++pos) + fprintf(wsaheader, "%d pos. word: %d\n", pos + 1, ((uint16*)&_wsaHeader)[pos]); + fprintf(wsaheader, "\n"); + for (uint32 pos = 0; pos < sizeof(_wsaHeader) / 4; ++pos) + fprintf(wsaheader, "%d pos. dword: %d\n", pos + 1, ((uint32*)&_wsaHeader)[pos]); + } + fclose(wsaheader); +#endif + + if (gameid == KYRA1CD) { + uint16 tmp = _wsaHeader._delta; + _wsaHeader._delta = _wsaHeader._type; + _wsaHeader._type = tmp; + + // skip 2 bytes + datastream.readUint16LE(); + } + + debug("_wsaHeader._numFrames = %d", _wsaHeader._numFrames); + debug("_wsaHeader._width = %d", _wsaHeader._width); + debug("_wsaHeader._height = %d", _wsaHeader._height); + debug("_wsaHeader._xPos = %d", _wsaHeader._xPos); + debug("_wsaHeader._yPos = %d", _wsaHeader._yPos); + debug("_wsaHeader._delta = %d", _wsaHeader._delta); + debug("_wsaHeader._type = %d", _wsaHeader._type); // check for version if (_wsaHeader._type) { @@ -55,27 +97,34 @@ namespace Kyra { offsetAdd = 768 /* 0x300 */; } - _frameCount = _wsaHeader._numFrames; + // last frame seems every time to be a empty one + _frameCount = _wsaHeader._numFrames - 1; _offsetTable = new uint32[_wsaHeader._numFrames + 2]; - assert(!_offsetTable); + assert(_offsetTable); // loads the offset table - for (uint32 tmp = 0; tmp < _wsaHeader._numFrames; ++tmp) { + for (uint32 tmp = 0; tmp < (uint32)_wsaHeader._numFrames + 2; ++tmp) { _offsetTable[tmp] = datastream.readUint32LE() + offsetAdd; } if (offsetAdd) { uint8* palbuffer = new uint8[offsetAdd]; - assert(!palbuffer); + assert(palbuffer); + + datastream.read(palbuffer, offsetAdd); _ownPalette = new Palette(palbuffer, offsetAdd); - assert(!_ownPalette); - } + assert(_ownPalette); + } + + // FIXME: Confirm the default value here? + _transparency = -1; } WSAMovieV1::~WSAMovieV1() { delete [] _buffer; delete [] _offsetTable; + delete [] _currentFrame; delete _ownPalette; } @@ -89,21 +138,215 @@ namespace Kyra { if (!_currentFrame) { _currentFrame = new uint8[_wsaHeader._width * _wsaHeader._height]; assert(_currentFrame); + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); } + if (frame >= _wsaHeader._numFrames) + return 0; + uint8* frameData = 0; - uint8 image40[64000]; // I think this will crash on Plam OS :) + static uint8 image40[64000]; // I think this will crash on Plam OS :) + memset(image40, 0, ARRAYSIZE(image40)); if (frame == _prefetchedFrame + 1) { - frameData = _buffer + _offsetTable[frame] + (hasPalette() ? 768 : 0); + frameData = _buffer + _offsetTable[frame]; Compression::decode80(frameData, image40); Compression::decode40(image40, _currentFrame); } else { - memset(_currentFrame, 0, _wsaHeader._width * _wsaHeader._height); + if (_background) { + setImageBackground(_background, _backWidth, _backHeight); + } else { + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); + } + + for (uint32 i = 0; i <= frame; ++i) + { + frameData = _buffer + _offsetTable[i]; + Compression::decode80(frameData, image40); + Compression::decode40(image40, _currentFrame); + } + } + + _prefetchedFrame = frame; + return _currentFrame; + } + + return 0; + } + + void WSAMovieV1::renderFrame(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 frame) { + if (!loadFrame(frame, 0, 0)) + return; + + uint8* src = _currentFrame; + uint8* dst = &plane[_wsaHeader._yPos * planepitch + _wsaHeader._xPos]; + uint32 copysize = planepitch - _wsaHeader._xPos; + + if (copysize > _wsaHeader._width) + copysize = _wsaHeader._width; + + if (_transparency == -1) { + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < planeheight; ++y_) { + memcpy(dst, src, copysize * sizeof(uint8)); + dst += planepitch; + src += _wsaHeader._width; + } + } else { + for (uint16 yadd = 0; yadd < _wsaHeader._height; ++yadd) { + for (uint16 xadd = 0; xadd < copysize; ++xadd) { + if (*src == _transparency) { + ++dst; + ++src; + } else { + *dst++ = *src++; + } + } - for (uint32 i = 0; i <= frame; i++) + src += _wsaHeader._width - copysize; + dst += planepitch - copysize; + } + } + } + + void WSAMovieV1::setImageBackground(uint8* plane, uint16 planepitch, uint16 height) { + assert(plane); + + _background = plane; + _backWidth = planepitch; _backHeight = height; + + if (!_currentFrame) { + _currentFrame = new uint8[_wsaHeader._width * _wsaHeader._height]; + assert(_currentFrame); + } + + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); + + uint8* src = &plane[_wsaHeader._yPos * planepitch + _wsaHeader._xPos]; + uint8* dst = _currentFrame; + uint32 copysize = planepitch - _wsaHeader._xPos; + + if (copysize > _wsaHeader._width) + copysize = _wsaHeader._width; + + // now copy the rect of the plane + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < height; ++y_) { + memcpy(dst, src, copysize * sizeof(uint8)); + dst += _wsaHeader._width; + src += planepitch; + } + + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < height; ++y_) { + for (uint16 x = 0; x < _wsaHeader._width; ++x) { + _currentFrame[y_ * _wsaHeader._width + x] ^= 0; + } + } + + _prefetchedFrame = 0xFFFE; + } + + // Kyrandia 2+ Movies + WSAMovieV2::WSAMovieV2(uint8* data, uint32 size) { + if (!data) { + error("resource created without data"); + } + + _background = 0; + _currentFrame = 0; + _ownPalette = 0; + _offsetTable = 0; + _prefetchedFrame = 0xFFFE; + _looping = false; + _buffer = data; + + // I like these Streams .... =) + Common::MemoryReadStream datastream(data, size); + + datastream.read(&_wsaHeader, sizeof(_wsaHeader)); + + // check for version + if (!_wsaHeader._type) { + error("loading a WSA version 1 with the WSA version 2 loader"); + } + + uint16 offsetAdd = 0; + + // checks now for own palette + if (_wsaHeader._type % 2) { + // don't now if this will work right, because a few lines before we use + // _wsaHeader._type for detect the version of the WSA movie, + // but this code was from FreeKyra Tools so I think it will work + + // if this is a packed palette we have a problem :) + offsetAdd = 768 /* 0x300 */; + } + + _offsetTable = new uint32[_wsaHeader._numFrames + 2]; + assert(_offsetTable); + + // loads the offset table + for (uint32 tmp = 0; tmp < (uint32)_wsaHeader._numFrames + 2; ++tmp) { + _offsetTable[tmp] = datastream.readUint32LE() + offsetAdd; + } + + if (offsetAdd) { + uint8* palbuffer = new uint8[offsetAdd]; + assert(palbuffer); + + datastream.read(palbuffer, offsetAdd); + + _ownPalette = new Palette(palbuffer, offsetAdd); + assert(_ownPalette); + } + + if (_offsetTable[_wsaHeader._numFrames + 1] - offsetAdd) { + ++_wsaHeader._numFrames; + _looping = true; + } + + _frameCount = _wsaHeader._numFrames; + } + + WSAMovieV2::~WSAMovieV2() { + delete [] _buffer; + delete [] _offsetTable; + delete [] _currentFrame; + delete _ownPalette; + } + + const uint8* WSAMovieV2::loadFrame(uint16 frame, uint16* width, uint16* height) { + if (width) *width = _wsaHeader._width; + if (height) *height = _wsaHeader._height; + + if (frame == _prefetchedFrame) { + return _currentFrame; + } else { + if (!_currentFrame) { + _currentFrame = new uint8[_wsaHeader._width * _wsaHeader._height]; + assert(_currentFrame); + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); + } + + if (frame >= _wsaHeader._numFrames) + return 0; + + uint8* frameData = 0; + static uint8 image40[64000]; // I think this will crash on Plam OS :) + memset(image40, 0, ARRAYSIZE(image40)); + + if (frame == _prefetchedFrame + 1) { + frameData = _buffer + _offsetTable[frame]; + Compression::decode80(frameData, image40); + Compression::decode40(image40, _currentFrame); + } else { + if (_background) { + setImageBackground(_background, _backWidth, _backHeight); + } else { + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); + } + + for (uint32 i = 0; i <= frame; ++i) { - frameData = _buffer + _offsetTable[i] + (hasPalette() ? 768 : 0); + frameData = _buffer + _offsetTable[i]; Compression::decode80(frameData, image40); Compression::decode40(image40, _currentFrame); } @@ -115,5 +358,75 @@ namespace Kyra { return 0; } + + void WSAMovieV2::renderFrame(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 frame) { + if (!loadFrame(frame, 0, 0)) + return; + + uint8* src = _currentFrame; + uint8* dst = &plane[_wsaHeader._yPos * planepitch + _wsaHeader._xPos]; + uint32 copysize = planepitch - _wsaHeader._xPos; + + if (copysize > _wsaHeader._width) + copysize = _wsaHeader._width; + + if (_transparency == -1) { + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < planeheight; ++y_) { + memcpy(dst, src, copysize * sizeof(uint8)); + dst += planepitch; + src += _wsaHeader._width; + } + } else { + for (uint16 yadd = 0; yadd < _wsaHeader._height; ++yadd) { + for (uint16 xadd = 0; xadd < copysize; ++xadd) { + if (*src == _transparency) { + ++dst; + ++src; + } else { + *dst++ = *src++; + } + } + + src += _wsaHeader._width - copysize; + dst += planepitch - copysize; + } + } + } + + void WSAMovieV2::setImageBackground(uint8* plane, uint16 planepitch, uint16 height) { + assert(plane); + + _background = plane; + _backWidth = planepitch; _backHeight = height; + + if (!_currentFrame) { + _currentFrame = new uint8[_wsaHeader._width * _wsaHeader._height]; + assert(_currentFrame); + } + + memset(_currentFrame, 0, sizeof(uint8) * _wsaHeader._width * _wsaHeader._height); + + uint8* src = &plane[_wsaHeader._yPos * planepitch + _wsaHeader._xPos]; + uint8* dst = _currentFrame; + uint32 copysize = planepitch - _wsaHeader._xPos; + + if (copysize > _wsaHeader._width) + copysize = _wsaHeader._width; + + // now copy the rect of the plane + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < height; ++y_) { + memcpy(dst, src, copysize * sizeof(uint8)); + dst += _wsaHeader._width; + src += planepitch; + } + + for (uint16 y_ = 0; y_ < _wsaHeader._height && _wsaHeader._yPos + y_ < height; ++y_) { + for (uint16 x = 0; x < _wsaHeader._width; ++x) { + _currentFrame[y_ * _wsaHeader._width + x] ^= 0; + } + } + + _prefetchedFrame = 0xFFFE; + } } // end of namespace Kyra diff --git a/kyra/wsamovie.h b/kyra/wsamovie.h index 2f23a5c4c2..62b7b8489d 100644 --- a/kyra/wsamovie.h +++ b/kyra/wsamovie.h @@ -31,50 +31,103 @@ namespace Kyra { public: - virtual ~Movie() {} + virtual ~Movie() { _transparency = -1; _ownPalette = 0; _frameCount = 0; } + virtual void renderFrame(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 frame) = 0; virtual const uint8* loadFrame(uint16 frame, uint16* width = 0, uint16* height = 0) = 0; virtual uint16 countFrames(void) { return _frameCount; } + // could be deleted(not imdiantly maybe it's needed sometime) + virtual void transparency(int16 color) { _transparency = color; } + virtual void position(uint16 x, uint16 y) = 0; + virtual bool hasPalette(void) { return (_ownPalette != 0); } virtual Palette* palette(void) { return _ownPalette; } + + virtual bool looping(void) { return false; } + virtual uint32 frameChange(void) { return 100; } + virtual void setImageBackground(uint8* plane, uint16 planepitch, uint16 height) {}; protected: + int16 _transparency; uint16 _frameCount; Palette* _ownPalette; }; // movie format for Kyrandia 1 - // there is also a new WSA Format for Kyrandia 2 - // which i will implement in future class WSAMovieV1 : public Movie { public: - WSAMovieV1(uint8* data, uint32 size); + WSAMovieV1(uint8* data, uint32 size, uint8 gameid); ~WSAMovieV1(); + void renderFrame(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 frame); const uint8* loadFrame(uint16 frame, uint16* width, uint16* height); + void setImageBackground(uint8* plane, uint16 planepitch, uint16 height); + + void position(uint16 x, uint16 y) { _wsaHeader._xPos = x; _wsaHeader._yPos = y; } protected: uint8* _buffer; #pragma START_PACK_STRUCTS + struct WSAHeader { + uint16 _numFrames; // All right + uint16 _width; // All right + uint16 _height; // All right + uint8 _xPos; // is wrong + uint8 _yPos; // is wrong + uint16 _delta; // should be right + uint16 _type; // should be right + } GCC_PACK _wsaHeader; +#pragma END_PACK_STRUCTS + + uint32* _offsetTable; + + uint8* _currentFrame; + uint16 _prefetchedFrame; + + uint8* _background; // only a pointer to the screen + uint16 _backWidth, _backHeight; + }; + + // movie format for Kyrandia 2+ + class WSAMovieV2 : public Movie { + + public: + WSAMovieV2(uint8* data, uint32 size); + ~WSAMovieV2(); + + void renderFrame(uint8* plane, uint16 planepitch, uint16 planeheight, uint16 frame); + const uint8* loadFrame(uint16 frame, uint16* width, uint16* height); + void setImageBackground(uint8* plane, uint16 planepitch, uint16 height); + + void position(uint16 x, uint16 y) { _wsaHeader._xPos = x; _wsaHeader._yPos = y; } + bool looping(void) { return _looping; } + protected: + + uint8* _buffer; + struct WSAHeader { uint16 _numFrames; // All right uint16 _width; // should be right uint16 _height; // should be right - uint8 _xPos; // could be wrong - uint8 _yPos; // could be wrong + uint16 _xPos; // could be wrong + uint16 _yPos; // could be wrong uint16 _delta; // could be wrong uint16 _type; // should be right } GCC_PACK _wsaHeader; -#pragma END_PACK_STRUCTS - + uint32* _offsetTable; uint8* _currentFrame; uint16 _prefetchedFrame; + + uint8* _background; // only a pointer to the screen + uint16 _backWidth, _backHeight; + + bool _looping; }; } // end of namespace Kyra -- cgit v1.2.3