From 7ba3dc1a302b7d1732cc870ae1bab5683ce802f7 Mon Sep 17 00:00:00 2001 From: Marisa-Chan Date: Thu, 14 Aug 2025 15:45:32 +0700 Subject: [PATCH] GAMOS: First working movie player --- POTFILES | 1 + configure.engine | 3 + console.cpp | 38 +++ console.h | 40 +++ credits.pl | 3 + detection.cpp | 45 ++++ detection.h | 69 +++++ detection_tables.h | 43 +++ file.cpp | 211 +++++++++++++++ file.h | 66 +++++ gamos.cpp | 660 +++++++++++++++++++++++++++++++++++++++++++++ gamos.h | 320 ++++++++++++++++++++++ keycodes.cpp | 284 +++++++++++++++++++ metaengine.cpp | 69 +++++ metaengine.h | 43 +++ module.mk | 22 ++ movie.cpp | 621 ++++++++++++++++++++++++++++++++++++++++++ movie.h | 108 ++++++++ music.cpp | 185 +++++++++++++ music.h | 67 +++++ proc.cpp | 80 ++++++ proc.h | 50 ++++ 22 files changed, 3028 insertions(+) create mode 100644 POTFILES create mode 100644 configure.engine create mode 100644 console.cpp create mode 100644 console.h create mode 100644 credits.pl create mode 100644 detection.cpp create mode 100644 detection.h create mode 100644 detection_tables.h create mode 100644 file.cpp create mode 100644 file.h create mode 100644 gamos.cpp create mode 100644 gamos.h create mode 100644 keycodes.cpp create mode 100644 metaengine.cpp create mode 100644 metaengine.h create mode 100644 module.mk create mode 100644 movie.cpp create mode 100644 movie.h create mode 100644 music.cpp create mode 100644 music.h create mode 100644 proc.cpp create mode 100644 proc.h diff --git a/POTFILES b/POTFILES new file mode 100644 index 0000000..1e1c41c --- /dev/null +++ b/POTFILES @@ -0,0 +1 @@ +engines/gamos/metaengine.cpp diff --git a/configure.engine b/configure.engine new file mode 100644 index 0000000..9e1e926 --- /dev/null +++ b/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components] +add_engine gamos "Gamos" no "" "" "highres" "midi" diff --git a/console.cpp b/console.cpp new file mode 100644 index 0000000..0f31c47 --- /dev/null +++ b/console.cpp @@ -0,0 +1,38 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "gamos/console.h" + +namespace Gamos { + +Console::Console() : GUI::Debugger() { + registerCmd("test", WRAP_METHOD(Console, Cmd_test)); +} + +Console::~Console() { +} + +bool Console::Cmd_test(int argc, const char **argv) { + debugPrintf("Test\n"); + return true; +} + +} // End of namespace Gamos diff --git a/console.h b/console.h new file mode 100644 index 0000000..e07209f --- /dev/null +++ b/console.h @@ -0,0 +1,40 @@ + +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef GAMOS_CONSOLE_H +#define GAMOS_CONSOLE_H + +#include "gui/debugger.h" + +namespace Gamos { + +class Console : public GUI::Debugger { +private: + bool Cmd_test(int argc, const char **argv); +public: + Console(); + ~Console() override; +}; + +} // End of namespace Gamos + +#endif // GAMOS_CONSOLE_H diff --git a/credits.pl b/credits.pl new file mode 100644 index 0000000..8400341 --- /dev/null +++ b/credits.pl @@ -0,0 +1,3 @@ +begin_section("Gamos"); + add_person("Name 1", "Handle 1", ""); +end_section(); diff --git a/detection.cpp b/detection.cpp new file mode 100644 index 0000000..0d980a7 --- /dev/null +++ b/detection.cpp @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "base/plugins.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/md5.h" +#include "common/str-array.h" +#include "common/translation.h" +#include "common/util.h" +#include "gamos/detection.h" +#include "gamos/detection_tables.h" + +const DebugChannelDef GamosMetaEngineDetection::debugFlagList[] = { + { Gamos::kDebugGraphics, "Graphics", "Graphics debug level" }, + { Gamos::kDebugPath, "Path", "Pathfinding debug level" }, + { Gamos::kDebugFilePath, "FilePath", "File path debug level" }, + { Gamos::kDebugScan, "Scan", "Scan for unrecognised games" }, + { Gamos::kDebugScript, "Script", "Enable debug script dump" }, + DEBUG_CHANNEL_END +}; + +GamosMetaEngineDetection::GamosMetaEngineDetection() : AdvancedMetaEngineDetection( + Gamos::gameDescriptions, Gamos::gamosGames) { +} + +REGISTER_PLUGIN_STATIC(GAMOS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, GamosMetaEngineDetection); diff --git a/detection.h b/detection.h new file mode 100644 index 0000000..a6d47a1 --- /dev/null +++ b/detection.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef GAMOS_DETECTION_H +#define GAMOS_DETECTION_H + +#include "engines/advancedDetector.h" + +namespace Gamos { + +enum GamosDebugChannels { + kDebugGraphics = 1, + kDebugPath, + kDebugScan, + kDebugFilePath, + kDebugScript, +}; + +extern const PlainGameDescriptor gamosGames[]; + +extern const ADGameDescription gameDescriptions[]; + +#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1 + +} // End of namespace Gamos + +class GamosMetaEngineDetection : public AdvancedMetaEngineDetection { + static const DebugChannelDef debugFlagList[]; + +public: + GamosMetaEngineDetection(); + ~GamosMetaEngineDetection() override {} + + const char *getName() const override { + return "gamos"; + } + + const char *getEngineName() const override { + return "Gamos"; + } + + const char *getOriginalCopyright() const override { + return "Gamos (C)"; + } + + const DebugChannelDef *getDebugChannels() const override { + return debugFlagList; + } +}; + +#endif // GAMOS_DETECTION_H diff --git a/detection_tables.h b/detection_tables.h new file mode 100644 index 0000000..bc041c4 --- /dev/null +++ b/detection_tables.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +namespace Gamos { + +const PlainGameDescriptor gamosGames[] = { + { "gamos", "Gamos" }, + { 0, 0 } +}; + +const ADGameDescription gameDescriptions[] = { + { + "solgamer", + 0, + AD_ENTRY1s("solgamer.exe", "6049dd1645071da1b60cdd395e6999ba", 24658521), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO1(GUIO_NONE) + }, + + AD_TABLE_END_MARKER +}; + +} // End of namespace Gamos diff --git a/file.cpp b/file.cpp new file mode 100644 index 0000000..3f08332 --- /dev/null +++ b/file.cpp @@ -0,0 +1,211 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#define FORBIDDEN_SYMBOL_EXCEPTION_printf + +#include "gamos/gamos.h" + +namespace Gamos { + +Archive::Archive() { +}; + +Archive::~Archive() { +}; + +bool Archive::open(const Common::Path &name) { + bool res = File::open(name); + + if (!res) + return false; + + seek(-12, SEEK_END); + + _dirOffset = 12 + readUint32LE(); + skip(4); + uint32 magic = readUint32LE(); + + if (magic != 0x3d53563d) // =VS= + return false; + + seek(-_dirOffset, SEEK_END); + + _dirCount = readUint32LE(); + _dataOffset = readUint32LE(); + + seek(-(_dirOffset + _dirCount * 5), SEEK_END); + + _directories.resize(_dirCount); + + for(uint i = 0; i < _dirCount; ++i) { + ArchiveDir &dir = _directories[i]; + + dir.offset = readUint32LE(); + dir.id = readByte(); + } + + return true; +} + +bool Archive::seekDir(uint id) { + int16 idx = findDirByID(id); + if (idx < 0) + return false; + + const ArchiveDir &dir = _directories[idx]; + + if ( !seek(_dataOffset + dir.offset, SEEK_SET) ) + return false; + + return true; +} + +int32 Archive::readPackedInt() { + byte b = readByte(); + if ( !(b & 0x80) ) + return b; + + byte num = 0; + byte skipsz = 0; + if ( !(b & 0x20) ) + num = b & 0x1f; + else + num = 1 + ((b >> 2) & 3); + + if (num > 4) { + skipsz = num - 4; + num = 4; + } + + int32 val = 0; + for(int i = 0; i < num; ++i) + val |= readByte() << (i << 3); + + if (skipsz) { + skip(skipsz); + /* warning !!!! */ + printf("readPackedInt skipped %d\n", skipsz); + } + + static int32 negs[4] {0, -1, -1025, -263169}; + static int32 adds[4] {0, 0x80, 0x480, 0x40480}; + + if (b & 0x20) { + val += (b & 3) * (1 << ((num << 3) & 0x1f)); + if (b & 0x10) + val = negs[num] - val; + else + val += adds[num]; + } + + return val; +} + +RawData *Archive::readCompressedData() { + RawData *data = new RawData(); + if (!readCompressedData(data)) { + delete data; + data = nullptr; + } + return data; +} + +bool Archive::readCompressedData(RawData *out) { + const byte t = readByte(); + if ((t & 0x80) == 0) + return false; + + _lastReadDecompressedSize = 0; + _lastReadSize = 0; + + if (t & 0x40) { + /* small uncompressed data */ + _lastReadSize = t & 0x1F; + } else { + /* read size */ + const byte szsize = (t & 3) + 1; + + /* big data size */ + for (uint i = 0; i < szsize; ++i) + _lastReadSize |= readByte() << (i << 3); + + /* is compressed */ + if (t & 0xC) { + for (uint i = 0; i < szsize; ++i) + _lastReadDecompressedSize |= readByte() << (i << 3); + } + } + + if (!_lastReadSize) + return false; + + _lastReadDataOffset = pos(); + out->resize(_lastReadSize); + read(out->data(), _lastReadSize); + + if (!_lastReadDecompressedSize) + return true; + + /* looks hacky but we just allocate array for decompressed and swap it with compressed */ + RawData compressed(_lastReadDecompressedSize); + out->swap(compressed); + + decompress(&compressed, out); + return true; +} + +void Archive::decompress(RawData const *in, RawData *out) { + uint pos = 0; + uint outPos = 0; + + while (pos < in->size()) { + byte ctrlBits = (*in)[pos]; + pos++; + + for(int bitsLeft = 8; bitsLeft > 0; --bitsLeft) { + if (pos >= in->size()) + return; + + if (ctrlBits & 1) { + (*out)[outPos] = (*in)[pos]; + outPos++; + pos++; + } else { + byte b1 = (*in)[pos]; + byte b2 = (*in)[pos + 1]; + pos += 2; + + byte num = (b2 & 0xF) + 3; + uint16 distance = b1 | ((b2 & 0xF0) << 4); + + for(int i = 0; i < num; ++i) { + (*out)[outPos] = (*out)[outPos - distance]; + outPos++; + } + } + + ctrlBits >>= 1; + } + } +} + + +}; \ No newline at end of file diff --git a/file.h b/file.h new file mode 100644 index 0000000..46b527b --- /dev/null +++ b/file.h @@ -0,0 +1,66 @@ +#ifndef GAMOS_FILE_H +#define GAMOS_FILE_H + +#include "common/file.h" + +namespace Gamos { + +typedef Common::Array RawData; + +struct ArchiveDir { + uint32 offset; + byte id; +}; + +class Archive : public Common::File { +public: + Archive(); + ~Archive() override; + bool open(const Common::Path &name) override; + + uint16 getDirCount() const { + return _dirCount; + } + + int16 findDirByID(uint id) const { + for (uint i = 0; i < _directories.size(); ++i) { + if (_directories[i].id == id) + return i; + } + + return -1; + } + + bool seekDir(uint id); + + int32 readPackedInt(); + + RawData *readCompressedData(); + bool readCompressedData(RawData *out); + + static void decompress(RawData const *in, RawData *out); + +public: + +uint32 _lastReadSize = 0; +uint32 _lastReadDecompressedSize = 0; +uint32 _lastReadDataOffset = 0; + + +private: + int32 _dirOffset; + + byte _dirCount; + uint32 _dataOffset; + + Common::Array _directories; + + + bool _error; +}; + + + +}; // namespace Gamos + +#endif // GAMOS_FILE_H \ No newline at end of file diff --git a/gamos.cpp b/gamos.cpp new file mode 100644 index 0000000..b0fcbf1 --- /dev/null +++ b/gamos.cpp @@ -0,0 +1,660 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#define FORBIDDEN_SYMBOL_EXCEPTION_printf +#define FORBIDDEN_SYMBOL_EXCEPTION_sprintf +#define FORBIDDEN_SYMBOL_EXCEPTION_exit +#define FORBIDDEN_SYMBOL_EXCEPTION_rand +#define FORBIDDEN_SYMBOL_EXCEPTION_FILE +#define FORBIDDEN_SYMBOL_EXCEPTION_fopen +#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite +#define FORBIDDEN_SYMBOL_EXCEPTION_fclose + + +#include "gamos/gamos.h" +#include "graphics/framelimiter.h" +#include "gamos/detection.h" +#include "gamos/console.h" +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/events.h" +#include "common/system.h" +#include "common/rect.h" +#include "engines/util.h" +#include "graphics/paletteman.h" +#include "common/keyboard.h" +#include "endian.h" +#include "audio/mididrv.h" +#include "audio/midiplayer.h" +#include + +namespace Gamos { + +GamosEngine *g_engine; + + +const byte GamosEngine::_xorKeys[32] = {0xa7, 0x15, 0xf0, 0x56, 0xf3, 0xfa, 0x84, 0x2c, + 0xfd, 0x81, 0x38, 0xac, 0x73, 0xd2, 0x22, 0x47, + 0xa0, 0x12, 0xb8, 0x19, 0x20, 0x6a, 0x26, 0x7c, + 0x32, 0x57, 0xdd, 0xb2, 0x38, 0xa7, 0x95, 0x7a}; + +GamosEngine::GamosEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst), + _gameDescription(gameDesc), _randomSource("Gamos") { + g_engine = this; +} + +GamosEngine::~GamosEngine() { + delete _screen; +} + +uint32 GamosEngine::getFeatures() const { + return _gameDescription->flags; +} + +Common::String GamosEngine::getGameId() const { + return _gameDescription->gameId; +} + +Common::Error GamosEngine::run() { + // Set the engine's debugger console + setDebugger(new Console()); + + // If a savegame was selected from the launcher, load it + int saveSlot = ConfMan.getInt("save_slot"); + if (saveSlot != -1) + (void)loadGameState(saveSlot); + + Common::String mname("solgamer.exe"); + init(mname); + + Common::Event e; + + Graphics::FrameLimiter limiter(g_system, 60); + while (!shouldQuit()) { + while (g_system->getEventManager()->pollEvent(e)) { + } + + // g_system->getPaletteManager()->setPalette(pal, 0, 256); + + limiter.delayBeforeSwap(); + _screen->update(); + limiter.startFrame(); + } + + return Common::kNoError; +} + +Common::Error GamosEngine::syncGame(Common::Serializer &s) { + // The Serializer has methods isLoading() and isSaving() + // if you need to specific steps; for example setting + // an array size after reading it's length, whereas + // for saving it would write the existing array's length + int dummy = 0; + s.syncAsUint32LE(dummy); + + return Common::kNoError; +} + +bool GamosEngine::loader2() { + int32 skipsz = _arch.readSint32LE(); + _arch.skip(skipsz); + + if (_arch.readByte() != 7) + return false; + + RawData data; + if (!_arch.readCompressedData(&data)) + return false; + + int32 p1 = 0; + int32 p2 = 0; + int32 pid = 0; + byte resType = 0; + int32 resSize = 0; + + Common::MemoryReadStream dataStream(data.data(), data.size()); + while (!dataStream.eos()) { + byte curByte = dataStream.readByte(); + + if (curByte == 0) { + break; + } else if (curByte == 0x80) { + p1 = 0; + p2 = 0; + pid = dataStream.readSint32LE(); + } else if (curByte == 1) { + p1 = dataStream.readSint32LE(); + } else if (curByte == 2) { + p2 = dataStream.readSint32LE(); + } else if (curByte == 7) { + int32 needsz = dataStream.readSint32LE(); // check free mem ? + printf("7777 want %d\n", needsz); + } else if (curByte == 0x40) { + resSize = 4; + resType = 0x40; + if (!loadResHandler(resType, pid, p1, p2, 0, data.data() + dataStream.pos(), resSize)) + return false; + + dataStream.skip(resSize); + } else if (curByte == 0x41 || curByte == 0x42) { + resSize = dataStream.readSint32LE(); + resType = curByte; + if (!loadResHandler(resType, pid, p1, p2, 0, data.data() + dataStream.pos(), resSize)) + return false; + + dataStream.skip(resSize); + } else if (curByte == 0x43) { + resSize = 0x10; + resType = 0x43; + if (!loadResHandler(resType, pid, p1, p2, 0, data.data() + dataStream.pos(), resSize)) + return false; + + dataStream.skip(resSize); + } else if (curByte == 0xff) { + printf("0xFF %d %d %d ", pid, p1, p2); + if (!reuseLastResource(resType, pid, p1, p2, 0)) + return false; + } else { + printf("loader2 want %x\n", curByte); + return false; + } + } + + return true; +} + +bool GamosEngine::loadModule(uint id) { + if ( (!_runReadDataMod && !initOrLoadSave(_saveLoadID)) || + !_arch.seekDir(1) ) + return false; + + _currentModuleID = id; + const byte targetDir = 2 + id; + + bool prefixLoaded = false; + byte prevByte = 0; + bool doLoad = true; + + int32 p1 = 0; + int32 p2 = 0; + int32 p3 = 0; + int32 pid = 0; + + while(doLoad) { + byte curByte = _arch.readByte(); + + switch(curByte) { + case 0: + if (prefixLoaded) { + doLoad = false; + break; + } + + prefixLoaded = true; + + if (!_arch.seekDir(targetDir)) + return false; + + break; + case CONFTP_P1: + p1 = _arch.readPackedInt(); + break; + case CONFTP_P2: + p2 = _arch.readPackedInt(); + break; + case CONFTP_P3: + p3 = _arch.readPackedInt(); + break; + case 4: { + _resReadOffset = _arch.pos(); + bool isResource = true; + if (prevByte == RESTP_F) { + RawData data; + if (!_arch.readCompressedData(&data)) + return false; + if (_runReadDataMod && BYTE_004177f7 == 0) + readData2(data); + if (BYTE_004177f7 == 0) { + //FUN_00403868(); + } + isResource = false; /* do not loadResHandler */ + } else if (prevByte == RESTP_10) { + if (!initMainDatas()) + return false; + isResource = false; /* do not loadResHandler */ + } else if (prevByte == RESTP_11) { + RawData data; + if (!_arch.readCompressedData(&data)) + return false; + if (pid == id) + readElementsConfig(data); + isResource = false; /* do not loadResHandler */ + } else if (prevByte == RESTP_18) { + /* free elements ? */ + } + + if (isResource) { + RawData data; + if (!_arch.readCompressedData(&data)) + return false; + + if (!loadResHandler(prevByte, pid, p1, p2, p3, data)) + return false; + } + + + /* memory management + if (prevByte == RESTP_43) { + + } else if (prevByte != RESTP_11 && prevByte != RESTP_20) { + // grow used space + } + */ + + break; + } + case 5: { + byte t = _arch.readByte(); + if (t == 0 || (t & 0xec) != 0xec) + return false; + + byte sz = (t & 3) + 1; + int32 movieSize = 0; + for(uint i = 0; i < sz; ++i) + movieSize |= _arch.readByte() << (i * 8); + + if (prevByte == 0x14) + _movieOffsets[pid] = _arch.pos(); + + _arch.skip(movieSize); + break; + } + case 6: + if (!loader2()) + return false; + break; + case 0xFF: + return false; + break; + default: + p1 = 0; + p2 = 0; + p3 = 0; + pid = 0; + prevByte = curByte & CONFTP_RESMASK; + + if ( (curByte & CONFTP_IDFLG) == 0 ) + pid = _arch.readPackedInt(); + + break; + } + } + + _screen->addDirtyRect(_screen->getBounds()); + _screen->update(); + + return true; +} + +bool GamosEngine::loadResHandler(uint tp, uint pid, uint p1, uint p2, uint p3, const byte *data, size_t dataSize) { + if (tp == RESTP_12) { + setFPS(_fps); + } else if (tp == RESTP_18) { + printf("18 size %d\n", dataSize); + } else if (tp == RESTP_40) { + return loadRes40(pid, data, dataSize); + } else if (tp == RESTP_41) { + return loadRes41(pid, data, dataSize); + } else if (tp == RESTP_42) { + return loadRes42(pid, p1, data, dataSize); + } else if (tp == RESTP_43) { + return loadRes43(pid, p1, p2, data, dataSize); + } else if (tp == RESTP_50) { + //printf("data 50 size %d\n", dataSize); + } else if (tp == RESTP_51) { + //printf("sound size %d\n", dataSize); + } else if (tp == RESTP_52) { + return loadRes52(pid, data, dataSize); + //printf("midi size %d\n", dataSize); + } else if (tp == RESTP_XORSEQ0) { + loadXorSeq(data, dataSize, 0); + } else if (tp == RESTP_XORSEQ1) { + loadXorSeq(data, dataSize, 1); + } else if (tp == RESTP_XORSEQ2) { + loadXorSeq(data, dataSize, 2); + } else { + //printf("Unk Res %x\n", tp); + } + return true; +} + +bool GamosEngine::loadResHandler(uint tp, uint pid, uint p1, uint p2, uint p3, const RawData &data) { + return loadResHandler(tp, pid, p1, p2, p3, data.data(), data.size()); +} + +bool GamosEngine::reuseLastResource(uint tp, uint pid, uint p1, uint p2, uint p3) { + if (tp == RESTP_43) { + _sprites[pid].sequences[p1][p2].image = _images.back(); + } else { + return false; + } + return true; +} + + +bool GamosEngine::initOrLoadSave(int32) { + return false; +} + +bool GamosEngine::initMainDatas() { + RawData rawdata; + + if (!_arch.readCompressedData(&rawdata)) + return false; + + Common::MemoryReadStream dataStream(rawdata.data(), rawdata.size(), DisposeAfterUse::NO); + + _magic = dataStream.readUint32LE(); + _pages1kbCount = dataStream.readUint32LE(); + _readBufSize = dataStream.readUint32LE(); + _width = dataStream.readUint32LE(); + _height = dataStream.readUint32LE(); + _unk2 = dataStream.readUint32LE(); + _unk3 = dataStream.readUint32LE(); + _movieCount = dataStream.readUint32LE(); + _unk5 = dataStream.readByte(); + _unk6 = dataStream.readByte(); + _unk7 = dataStream.readByte(); + _fps = dataStream.readByte(); + _unk8 = dataStream.readByte(); + _unk9 = dataStream.readByte(); + _fadeEffectID = dataStream.readByte(); + _unk11 = dataStream.readByte(); + + /*_winX = dataStream.readUint32LE(); + _winY = dataStream.readUint32LE(); + _winW = dataStream.readUint32LE(); + _winH = dataStream.readUint32LE();*/ + dataStream.skip(16); + + int64 pos = dataStream.pos(); + _string1 = dataStream.readString(0, 64); + dataStream.seek(pos + 64); + _winCaption = dataStream.readString(0, 9); + + if (!_screen) { + initGraphics(_width, _height); + _screen = new Graphics::Screen(); + } + + _movieOffsets.clear(); + _movieOffsets.resize(_movieCount, 0); + + return true; +} + +bool GamosEngine::init(const Common::String &moduleName) { + BYTE_004177f7 = 0; + + if (!_arch.open(Common::Path(moduleName))) + return false; + + if (!loadInitModule()) + return false; + + + if (!playIntro()) + return false; + + return true; +} + +bool GamosEngine::loadInitModule() { + + _runReadDataMod = true; + + return loadModule(0); +} + +void GamosEngine::setFPS(uint fps) { + _delayTime = 0; + if (fps) + _delayTime = 1000 / fps; +} + +void GamosEngine::readElementsConfig(const RawData &data) { + Common::MemoryReadStream dataStream(data.data(), data.size(), DisposeAfterUse::NO); + + uint32 bkgnum1 = dataStream.readUint32LE(); // 0 + uint32 bkgnum2 = dataStream.readUint32LE(); // 4 + dataStream.readUint32LE(); // 8 + dataStream.readUint32LE(); // c + dataStream.readUint32LE(); // 10 + dataStream.readUint32LE(); // 14 + dataStream.readUint32LE(); // 18 + dataStream.readUint32LE(); // 1c + dataStream.readUint32LE(); // 20 + uint32 imageCount = dataStream.readUint32LE(); // 24 + dataStream.readUint32LE(); // 28 + uint32 midiCount = dataStream.readUint32LE(); // 2c + dataStream.readUint32LE(); // 30 + + _bkgImages.clear(); + _bkgImages.resize(bkgnum1 * bkgnum2); + + _sprites.clear(); + _sprites.resize(imageCount); + + _midiTracks.clear(); + _midiTracks.resize(midiCount); +} + +void GamosEngine::loadXorSeq(const byte *data, size_t dataSize, int id) { + Common::MemoryReadStream dataStream(data, dataSize); + + Common::Array &seq = _xorSeq[id]; + + uint32 num = dataStream.readUint32LE(); + seq.resize(num); + + for(uint i = 0; i < num; ++i) { + seq[i].len = dataStream.readUint32LE(); + seq[i].pos = dataStream.readUint32LE(); + } +} + +bool GamosEngine::loadRes40(int32 id, const byte *data, size_t dataSize) { + if (dataSize < 4) + return false; + + _sprites[id].field_0 = data[0]; + _sprites[id].field_1 = data[1]; + _sprites[id].field_2 = data[2]; + _sprites[id].field_3 = data[3]; + + return true; +} + +bool GamosEngine::loadRes41(int32 id, const byte *data, size_t dataSize) { + if (*(const uint32 *)data != 0) { + printf("41 not null!!!\n"); + exit(0); + } + _sprites[id].sequences.resize(dataSize / 4); + return true; +} + +bool GamosEngine::loadRes42(int32 id, int32 p1, const byte *data, size_t dataSize) { + //printf("loadRes42 pid %d p %d sz %x\n",id, p1, dataSize); + + if (_sprites[id].sequences.size() == 0) + _sprites[id].sequences.resize(1); + + int32 count = dataSize / 8; + _sprites[id].sequences[p1].resize(count); + + Common::MemoryReadStream strm(data, dataSize); + for(int i = 0; i < count; ++i) { + int32 dataz = strm.readSint32LE(); + if (dataz != 0) { + printf("42 nut null \n"); + exit(0); + } + + ImagePos &imgpos = _sprites[id].sequences[p1][i]; + imgpos.xoffset = strm.readSint16LE(); + imgpos.yoffset = strm.readSint16LE(); + } + return true; +} + +bool GamosEngine::loadRes43(int32 id, int32 p1, int32 p2, const byte *data, size_t dataSize) { + _images.push_back( new Image() ); + _sprites[id].sequences[p1][p2].image = _images.back(); + + Image *img = _sprites[id].sequences[p1][p2].image; + + Common::MemoryReadStream s(data, dataSize); + img->surface.pitch = img->surface.w = s.readSint16LE(); + img->surface.h = s.readSint16LE(); + img->loaded = false; + + uint32 token = s.readUint32LE(); + + /* token 'Disk' */ + if (token == 0x4469736b) { + img->offset = s.readSint32LE(); + img->cSize = s.readSint32LE(); + } else { + if (_sprites[id].field_1 & 0x80) { + img->offset = _arch._lastReadDataOffset; + img->cSize = _arch._lastReadSize; + } else { + img->loaded = true; + img->rawData.assign(data + 4, data + dataSize); + img->surface.setPixels(img->rawData.data()); + img->surface.format = Graphics::PixelFormat::createFormatCLUT8(); + } + } + + return true; +} + +bool GamosEngine::loadRes52(int32 id, const byte *data, size_t dataSize) { + _midiTracks[id].assign(data, data + dataSize); + return true; +} + + +bool GamosEngine::playIntro() { + if (_movieCount != 0 && _unk11 == 1) + return scriptFunc18(0); + return true; +} + + +bool GamosEngine::scriptFunc18(int id) { + if (true) { + _isMoviePlay++; + bool res = _moviePlayer.playMovie(&_arch, _movieOffsets[id], this); + _isMoviePlay--; + return res; + } + + return true; +} + +void GamosEngine::stopMidi() { + _musicPlayer.stopMusic(); + _midiStarted = false; +} + +void GamosEngine::stopMCI() { + +} + +void GamosEngine::stopSounds() { + +} + + + +void GamosEngine::setErrMessage(const Common::String &msg) { + if (_errSet) + return; + + _errMessage = msg; + _errSet = true; +} + +void GamosEngine::updateScreen(bool checkers, Common::Rect rect) { + if (_width == 0 || _height == 0) + return; + + if (!checkers) { + _screen->addDirtyRect(rect); + return; + } + + static const Common::Point checkerCoords[16] = { + {0, 0}, {16, 32}, {48, 16}, {16, 48}, + {0, 32}, {32, 48}, {16, 16}, {48, 0}, + {32, 32}, {0, 48}, {32, 16}, {16, 0}, + {48, 32}, {32, 0}, {0, 16}, {48, 48}}; + + const int16 maxDelay = (500 / 10) - 1; + + for (int16 p = 0; p < 16; p++) { + uint32 val = g_system->getMillis(); + const Common::Point point = checkerCoords[p]; + for (uint32 x = point.x; x < _width; x += 64) { + for (uint32 y = point.y; y < _height; y += 64) { + _screen->addDirtyRect(Common::Rect(x, y, x + 16, y + 16)); + } + } + _screen->update(); + val = g_system->getMillis() - val; + + if (val > maxDelay) + g_system->delayMillis(maxDelay - val); + } +} + + +void GamosEngine::readData2(const RawData &data) { + Common::MemoryReadStream dataStream(data.data(), data.size()); + dataStream.skip(4); // FIX ME + _messageProc._gd2flags = dataStream.readByte(); + dataStream.skip(0x40 - 5); // FIX ME + for (int i = 0; i < 12; i++) { + _messageProc._keyCodes[i] = _winkeyMap[dataStream.readByte()]; + } +} + + +void GamosEngine::playMidi(Common::Array *buffer) { + _musicPlayer.stopMusic(); + _musicPlayer.playMusic(buffer); + _midiStarted = true; +} + +} // End of namespace Gamos diff --git a/gamos.h b/gamos.h new file mode 100644 index 0000000..76fb832 --- /dev/null +++ b/gamos.h @@ -0,0 +1,320 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef GAMOS_H +#define GAMOS_H + +#include "common/events.h" +#include "common/rect.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/error.h" +#include "common/fs.h" +#include "common/hash-str.h" +#include "common/random.h" +#include "common/serializer.h" +#include "common/util.h" +#include "common/array.h" +#include "common/memstream.h" +#include "engines/engine.h" +#include "engines/savestate.h" +#include "graphics/screen.h" + +#include "gamos/proc.h" +#include "gamos/music.h" +#include "gamos/movie.h" + + +#include "gamos/detection.h" +#include "gamos/file.h" + +namespace Gamos { + +struct GamosGameDescription; + +enum CONFTYPE { + CONFTP_P1 = 1, + CONFTP_P2 = 2, + CONFTP_P3 = 3, + CONFTP_IDFLG = 0x80, + CONFTP_RESMASK = 0x7f, + +}; + +enum ACT2 { + ACT_NONE = 0xe, + ACT2_81 = 0x81, + ACT2_82 = 0x82, + ACT2_83 = 0x83, + ACT2_84 = 0x84, + ACT2_8f = 0x8f, +}; + +enum RESTYPE { + RESTP_F = 0xf, + RESTP_10 = 0x10, + RESTP_11 = 0x11, + RESTP_12 = 0x12, + RESTP_18 = 0x18, + RESTP_19 = 0x19, + RESTP_20 = 0x20, + RESTP_40 = 0x40, + RESTP_41 = 0x41, + RESTP_42 = 0x42, + RESTP_43 = 0x43, + RESTP_50 = 0x50, + RESTP_51 = 0x51, + RESTP_52 = 0x52, + RESTP_XORSEQ0 = 0x7c, + RESTP_XORSEQ1 = 0x7d, + RESTP_XORSEQ2 = 0x7e, +}; + +struct BkgImage { + bool loaded = false; + uint32 offset = 0; + Graphics::Surface surface; + + RawData rawData; +}; + +struct Image { + bool loaded = false; + int32 offset = 0; + int32 size = 0; + int32 cSize = 0; + + Graphics::Surface surface; + + RawData rawData; +}; + +struct ImagePos { + int16 xoffset = 0; + int16 yoffset = 0; + + Image *image = nullptr; +}; + +typedef Common::Array ImageSeq; + +struct Sprite { + byte field_0; + byte field_1; + byte field_2; + byte field_3; + + Common::Array sequences; +}; + +/* Used to xor savedata */ +struct XorArg { + uint32 pos; + uint32 len; +}; + +class GamosEngine : public Engine { +friend class MoviePlayer; + +private: + const ADGameDescription *_gameDescription; + Common::RandomSource _randomSource; + + bool _errSet = false;; + Common::String _errMessage; + + Archive _arch; + + byte _cmdByte; + + bool _runReadDataMod; + bool _currentModuleID; + + byte _saveLoadID; + + uint32 _magic; + uint32 _pages1kbCount; + uint32 _readBufSize; + uint32 _width; + uint32 _height; + uint32 _unk2; + uint32 _unk3; + uint32 _movieCount; + byte _unk5; + byte _unk6; + byte _unk7; + byte _fps; + byte _unk8; + byte _unk9; + byte _fadeEffectID; + byte _unk11; + + int _isMoviePlay = 0; + + bool _onlyScanImage = false; + int32 _resReadOffset = 0; + + Common::String _string1; + Common::String _winCaption; + + Common::Array _movieOffsets; + + Common::Array _images; + + Common::Array _bkgImages; + + Common::Array _sprites; + + Common::Array< Common::Array > _midiTracks; + + uint32 _delayTime = 0; + + Common::Array _xorSeq[3]; + + static const byte _xorKeys[32]; + + + + uint8 BYTE_004177f7 = 0; + + bool _midiStarted = false; + + /* Data2 */ + + + + MidiMusic _musicPlayer; + SystemProc _messageProc; + MoviePlayer _moviePlayer; + +private: + static const uint16 _winkeyMap[256]; + +protected: + // Engine APIs + Common::Error run() override; + + void readCMDByte() { + _cmdByte = _arch.readByte(); + } + + bool loadModule(uint id); + bool loader2(); + + bool loadResHandler(uint tp, uint pid, uint p1, uint p2, uint p3, const byte *data, size_t dataSize); + bool loadResHandler(uint tp, uint pid, uint p1, uint p2, uint p3, const RawData &data); + + bool reuseLastResource(uint tp, uint pid, uint p1, uint p2, uint p3); + + bool initOrLoadSave(int32); + + bool initMainDatas(); + + bool init(const Common::String &moduleName); + bool loadInitModule(); + + void readElementsConfig(const RawData &data); + + void setFPS(uint fps); + + void loadXorSeq(const byte *data, size_t dataSize, int id); + + bool loadRes40(int32 id, const byte *data, size_t dataSize); + bool loadRes41(int32 id, const byte *data, size_t dataSize); + bool loadRes42(int32 id, int32 p1, const byte *data, size_t dataSize); + bool loadRes43(int32 id, int32 p1, int32 p2, const byte *data, size_t dataSize); + + bool loadRes52(int32 id, const byte *data, size_t dataSize); + + + void playMidi(Common::Array *buffer); + + void stopMidi(); + void stopMCI(); + void stopSounds(); + + bool playIntro(); + + bool scriptFunc18(int id); + + void setErrMessage(const Common::String &msg); + + void updateScreen(bool checkers, Common::Rect rect); + + void readData2(const RawData &data); + + +public: + Graphics::Screen *_screen = nullptr; +public: + GamosEngine(OSystem *syst, const ADGameDescription *gameDesc); + ~GamosEngine() override; + + uint32 getFeatures() const; + + /** + * Returns the game Id + */ + Common::String getGameId() const; + + /** + * Gets a random number + */ + uint32 getRandomNumber(uint maxNum) { + return _randomSource.getRandomNumber(maxNum); + } + + bool hasFeature(EngineFeature f) const override { + return + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime) || + (f == kSupportsReturnToLauncher); + }; + + bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { + return true; + } + bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { + return true; + } + + /** + * Uses a serializer to allow implementing savegame + * loading and saving using a single method + */ + Common::Error syncGame(Common::Serializer &s); + + Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override { + Common::Serializer s(nullptr, stream); + return syncGame(s); + } + Common::Error loadGameStream(Common::SeekableReadStream *stream) override { + Common::Serializer s(stream, nullptr); + return syncGame(s); + } +}; + +extern GamosEngine *g_engine; +#define SHOULD_QUIT ::Gamos::g_engine->shouldQuit() + +} // End of namespace Gamos + +#endif // GAMOS_H diff --git a/keycodes.cpp b/keycodes.cpp new file mode 100644 index 0000000..491b569 --- /dev/null +++ b/keycodes.cpp @@ -0,0 +1,284 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "gamos/gamos.h" +namespace Gamos { + +const uint16 GamosEngine::_winkeyMap[256] = { + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_CANCEL, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_BACKSPACE, + Common::KEYCODE_TAB, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_RETURN, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_MENU, + Common::KEYCODE_PAUSE, + Common::KEYCODE_CAPSLOCK, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_ESCAPE, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_SPACE, + Common::KEYCODE_PAGEUP, + Common::KEYCODE_PAGEDOWN, + Common::KEYCODE_END, + Common::KEYCODE_HOME, + Common::KEYCODE_LEFT, + Common::KEYCODE_UP, + Common::KEYCODE_RIGHT, + Common::KEYCODE_DOWN, + Common::KEYCODE_SELECT, + Common::KEYCODE_PRINT, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INSERT, + Common::KEYCODE_DELETE, + Common::KEYCODE_HELP, + Common::KEYCODE_0, + Common::KEYCODE_1, + Common::KEYCODE_2, + Common::KEYCODE_3, + Common::KEYCODE_4, + Common::KEYCODE_5, + Common::KEYCODE_6, + Common::KEYCODE_7, + Common::KEYCODE_8, + Common::KEYCODE_9, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_a, + Common::KEYCODE_b, + Common::KEYCODE_c, + Common::KEYCODE_d, + Common::KEYCODE_e, + Common::KEYCODE_f, + Common::KEYCODE_g, + Common::KEYCODE_h, + Common::KEYCODE_i, + Common::KEYCODE_j, + Common::KEYCODE_k, + Common::KEYCODE_l, + Common::KEYCODE_m, + Common::KEYCODE_n, + Common::KEYCODE_o, + Common::KEYCODE_p, + Common::KEYCODE_q, + Common::KEYCODE_r, + Common::KEYCODE_s, + Common::KEYCODE_t, + Common::KEYCODE_u, + Common::KEYCODE_v, + Common::KEYCODE_w, + Common::KEYCODE_x, + Common::KEYCODE_y, + Common::KEYCODE_z, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_SLEEP, + Common::KEYCODE_KP0, + Common::KEYCODE_KP1, + Common::KEYCODE_KP2, + Common::KEYCODE_KP3, + Common::KEYCODE_KP4, + Common::KEYCODE_KP5, + Common::KEYCODE_KP6, + Common::KEYCODE_KP7, + Common::KEYCODE_KP8, + Common::KEYCODE_KP9, + Common::KEYCODE_KP_MULTIPLY, + Common::KEYCODE_KP_PLUS, + Common::KEYCODE_INVALID, + Common::KEYCODE_KP_MINUS, + Common::KEYCODE_INVALID, + Common::KEYCODE_KP_DIVIDE, + Common::KEYCODE_F1, + Common::KEYCODE_F2, + Common::KEYCODE_F3, + Common::KEYCODE_F4, + Common::KEYCODE_F5, + Common::KEYCODE_F6, + Common::KEYCODE_F7, + Common::KEYCODE_F8, + Common::KEYCODE_F9, + Common::KEYCODE_F10, + Common::KEYCODE_F11, + Common::KEYCODE_F12, + Common::KEYCODE_F13, + Common::KEYCODE_F14, + Common::KEYCODE_F15, + Common::KEYCODE_F16, + Common::KEYCODE_F17, + Common::KEYCODE_F18, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_NUMLOCK, + Common::KEYCODE_SCROLLOCK, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID, + Common::KEYCODE_INVALID +}; + +} diff --git a/metaengine.cpp b/metaengine.cpp new file mode 100644 index 0000000..8768bc8 --- /dev/null +++ b/metaengine.cpp @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "common/translation.h" + +#include "gamos/metaengine.h" +#include "gamos/detection.h" +#include "gamos/gamos.h" + +namespace Gamos { + +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_ORIGINAL_SAVELOAD, + { + _s("Use original save/load screens"), + _s("Use the original save/load screens instead of the ScummVM ones"), + "original_menus", + false, + 0, + 0 + } + }, + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + +} // End of namespace Gamos + +const char *GamosMetaEngine::getName() const { + return "gamos"; +} + +const ADExtraGuiOptionsMap *GamosMetaEngine::getAdvancedExtraGuiOptions() const { + return Gamos::optionsList; +} + +Common::Error GamosMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + *engine = new Gamos::GamosEngine(syst, desc); + return Common::kNoError; +} + +bool GamosMetaEngine::hasFeature(MetaEngineFeature f) const { + return checkExtendedSaves(f) || + (f == kSupportsLoadingDuringStartup); +} + +#if PLUGIN_ENABLED_DYNAMIC(GAMOS) +REGISTER_PLUGIN_DYNAMIC(GAMOS, PLUGIN_TYPE_ENGINE, GamosMetaEngine); +#else +REGISTER_PLUGIN_STATIC(GAMOS, PLUGIN_TYPE_ENGINE, GamosMetaEngine); +#endif diff --git a/metaengine.h b/metaengine.h new file mode 100644 index 0000000..c0ad7ad --- /dev/null +++ b/metaengine.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef GAMOS_METAENGINE_H +#define GAMOS_METAENGINE_H + +#include "engines/advancedDetector.h" + +class GamosMetaEngine : public AdvancedMetaEngine { +public: + const char *getName() const override; + + Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override; + + /** + * Determine whether the engine supports the specified MetaEngine feature. + * + * Used by e.g. the launcher to determine whether to enable the Load button. + */ + bool hasFeature(MetaEngineFeature f) const override; + + const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override; +}; + +#endif // GAMOS_METAENGINE_H diff --git a/module.mk b/module.mk new file mode 100644 index 0000000..d65d1a9 --- /dev/null +++ b/module.mk @@ -0,0 +1,22 @@ +MODULE := engines/gamos + +MODULE_OBJS = \ + gamos.o \ + file.o \ + console.o \ + metaengine.o \ + keycodes.o \ + music.o \ + proc.o \ + movie.o + +# This module can be built as a plugin +ifeq ($(ENABLE_GAMOS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk + +# Detection objects +DETECT_OBJS += $(MODULE)/detection.o diff --git a/movie.cpp b/movie.cpp new file mode 100644 index 0000000..9c703d7 --- /dev/null +++ b/movie.cpp @@ -0,0 +1,621 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + #define FORBIDDEN_SYMBOL_EXCEPTION_printf + +#include "gamos/gamos.h" + +namespace Gamos { + +bool MoviePlayer::init(Common::File *file, uint32 offset, GamosEngine *gamos) { + _gamos = gamos; + _screen = _gamos->_screen; + _messageProc = &_gamos->_messageProc; + + + _loopCount = 1; + _pos = Common::Point(); + _midiBufferSize = 0; + _soundBufferSize = 0; + _paletteBufferSize = 0; + _bufferSize = 0; + _packedBufferSize = 0; + _frameTime = 0; + _loopPoint = 0; + _midiBuffer.clear(); + _soundBuffer.clear(); + _paletteBuffer.clear(); + _buffer.clear(); + _packedBuffer.clear(); + _midiStarted = false; + _soundPlaying = false; + _frameSize = Common::Point(_screen->w, _screen->h); + + _gamos->stopMidi(); + _gamos->stopMCI(); + + _file = file; + return _file->seek(offset, SEEK_SET); +} + +bool MoviePlayer::deinit() { + if (_soundPlaying) + _gamos->stopSounds(); + + _gamos->stopMidi(); + _gamos->stopMCI(); + + _file = nullptr; + return true; +} + +bool MoviePlayer::playMovie(Common::File *file, uint32 offset, GamosEngine *gamos) { + if (!init(file, offset, gamos)) + return error(); + + while (true) { + int status = 0; + + readHdr(); + + switch(_hdrBytes[0]) { + case 0: + status = processControlChunk(); + break; + + case 1: + status = processImageChunk(); + break; + + case 2: + status = processPaletteChunk(); + break; + + case 3: + status = processSoundChunk(); + break; + + case 4: + status = proccessMidiChunk(); + break; + + default: + break; + } + + while(true) + { + if (status == 2) { + while(true) { + if ( !readHdr() ) + return error(); + if (_hdrBytes[0] == 0) { + if (_hdrBytes[1] == 0 || _hdrBytes[1] == 1) + break; + } else { + if (!_file->seek(_hdrValue1, SEEK_CUR)) + return error(); + } + } + status = processControlChunk(); + } else if (status == 0) { + return error(); + } else if (status == 3) { + return deinit(); + } else { + break; + } + } + } + + return deinit(); +} + +bool MoviePlayer::error() { + deinit(); + _gamos->setErrMessage("Movie playback error."); + return false; +} + +int MoviePlayer::processControlChunk() { + printf("%x movieProcessControl %d\n", _file->pos(), _hdrBytes[1]); + + switch(_hdrBytes[1]) { + case 0: + if ((uint32_t)_hdrValue1 != 0x563d2d5b || (uint32_t)_hdrValue2 != 0x5d2d3d53) { + error(); + return 0; + } + return 3; + + case 1: + _loopCount = 1; + _loopPoint = 0; + + if (_hdrBytes[2] != 0) + _loopCount = _hdrValue1; + + if (_hdrBytes[3] != 0) + _frameTime = _hdrValue2; + break; + + case 2: + if (_hdrBytes[2] != 0) { + _packedBufferSize = _hdrValue1; + _packedBuffer.resize(_hdrValue1); + } + break; + + case 3: + if (_hdrBytes[2] != 0) { + _bufferSize = _hdrValue1; + _buffer.resize(_hdrValue1); + } + if (_hdrBytes[3] != 0) { + _paletteBufferSize = _hdrValue2; + _paletteBuffer.resize(_hdrValue2); + } + break; + + case 4: + if (_hdrBytes[2] != 0) { + _soundBufferSize = _hdrValue1; + _soundBuffer.resize(_hdrValue1); + } + if (_hdrBytes[3] != 0) { + _midiBufferSize = _hdrValue2; + _midiBuffer.resize(_hdrValue2); + } + break; + + case 5: + if (_hdrBytes[2] != 0) { + _pos.x = _hdrValue1; + } + if (_hdrBytes[3] != 0) { + _pos.y = _hdrValue2; /* BUG? Originally here same _pos.x */ + } + break; + + case 6: + if (_hdrBytes[2] != 0) { + _frameSize.x = _hdrValue1; + } + if (_hdrBytes[3] != 0) { + _frameSize.y = _hdrValue2; + } + break; + + } + return 1; +} + +int MoviePlayer::processImageChunk() { + printf("%x movieProcessImageChunk %d\n", _file->pos(), _hdrValue1); + if (!readCompressed(_bufferSize, &_buffer)) + return 0; + + bool keepAct = true; + + if (_hdrBytes[1] == 1) { + _forceStopMidi = false; + //waveoutrestart() + _screen->fillRect(_screen->getBounds(), _hdrBytes[3]); + if (_loopCount > 1) + _loopPoint = _file->pos(); + keepAct = false; + _doUpdateScreen = false; + } + else if (_hdrBytes[1] == 2) { + _forceStopMidi = true; + _loopCount--; + if (_loopCount != 0) + _file->seek(_loopPoint, 0); + } + + if (_hdrValue1 != 0) { + byte *pdata = _buffer.data(); + Common::Point xy; + Common::Point wh; + + while (true) { + byte val = *pdata; + pdata++; + if ( (val & 0x40) == 0 ) { + xy.x = _pos.x + *pdata; + pdata++; + + if ( (val & 4) != 0 ) { + xy.x += *pdata * 256; + pdata++; + } + + xy.y = _pos.y + *pdata; + pdata++; + + if ( (val & 8) != 0 ) { + xy.y += *pdata * 256; + pdata++; + } + + wh.x = *pdata; + pdata++; + + if ( (val & 0x10) != 0 ) { + wh.x += *pdata * 256; + pdata++; + } + + wh.y = *pdata; + pdata++; + + if ( (val & 0x20) != 0 ) { + wh.y += *pdata * 256; + pdata++; + } + } else { + xy = _pos; + wh = _frameSize; + } + + printf("movie blit%d %d %d %d %d\n", val & 3, xy.x, xy.y, wh.x, wh.y); + static byte *(*blitters[4])(Common::Rect, byte *, Graphics::Surface *) = + {&blit0, + &blit1, + &blit2, + &blit3}; + + pdata = blitters[val & 3](Common::Rect(xy, xy + wh), pdata, _screen->surfacePtr()); + + if (_doUpdateScreen) { + _gamos->updateScreen(false, Common::Rect(xy, xy + wh)); + } + + if (val & 0x80) + break; + } + + } + + if (_doUpdateScreen) { + //FUN_00403c70(true); + } + + uint32 tstamp = 0; + uint8 act = processMessages(keepAct, &tstamp); + + if (act == ACT2_82) + return 2; + + if (act == ACT2_83) + return 3; + + if (_hdrBytes[1] == 1) { + _gamos->updateScreen(_gamos->_fadeEffectID != 0, Common::Rect(_pos, _pos + _frameSize)); + + _firstFrameTime = g_system->getMillis(); + _currentFrame = 0; + _skippedFrames = 0; + _doUpdateScreen = true; + } else if (_frameTime == 0) { + _doUpdateScreen = true; + } else { + int32 dtime = (tstamp - _firstFrameTime) / _currentFrame; + if (dtime > _frameTime) { + if (_soundBufferSize) { + _skippedFrames++; + if (_skippedFrames != 8) + _doUpdateScreen = false; + } + } + else if (dtime < _frameTime) { + while (true) { + act = processMessages(false, &tstamp); + if (act == ACT2_82) + return 2; + + if (act == ACT2_83) + return 3; + + if ((tstamp - _firstFrameTime) / _currentFrame >= _frameTime) + break; + + g_system->delayMillis(1); + } + } + + _skippedFrames = 0; + _doUpdateScreen = true; + } + + _screen->update(); + _currentFrame++; + + return 1; +} + +int MoviePlayer::processPaletteChunk() { + printf("%x movieProcessPaletteChunk\n", _file->pos()); + if (!readCompressed(_paletteBufferSize, &_paletteBuffer)) + return 0; + + _screen->setPalette(_paletteBuffer.data()); + //g_system->getPaletteManager()->setPalette(PalColors.data(), 0, 256); + + return 1; +} + +int MoviePlayer::processSoundChunk() { + printf("%x movieProcessSoundChunk\n", _file->pos()); + if (!readCompressed(_soundBufferSize, &_soundBuffer)) + return 0; + return 1; +} + +int MoviePlayer::proccessMidiChunk() { + printf("%x movieProccessMidiChunk\n", _file->pos()); + + if (_midiStarted && (_forceStopMidi == false || _hdrBytes[1] != 0)) { + _gamos->stopMidi(); + _midiStarted = false; + } + + if (_hdrValue1 == 0) + return 1; + + if (_midiStarted) { + if ( !_file->seek(_hdrValue1, SEEK_CUR) ) + return 0; + return 1; + } + + if (!readCompressed(_midiBufferSize, &_midiBuffer)) { + _midiStarted = false; + return 0; + } + + _gamos->playMidi(&_midiBuffer); + _midiStarted = true; + + return 1; +} + +bool MoviePlayer::readHdr() { + _file->read(_hdrBytes, 4); + _hdrValue1 = _file->readSint32LE(); + _hdrValue2 = _file->readSint32LE(); + return true; +} + +bool MoviePlayer::readCompressed(int32_t count, Common::Array *buf) { + if (_hdrValue1 == 0) + return true; + + if (_hdrValue1 != _hdrValue2) { + _packedBuffer.resize(_hdrValue1); + _file->read(_packedBuffer.data(), _hdrValue1); + buf->resize(_hdrValue2); + Archive::decompress(&_packedBuffer, buf); + } + else { + buf->resize(_hdrValue1); + _file->read(buf->data(), _hdrValue1); + } + return true; +} + +byte* MoviePlayer::blit0(Common::Rect rect, byte *in, Graphics::Surface *surface) { + int16 y = rect.top; + while (y < rect.bottom) { + const int count = rect.width(); + byte *out = (byte *)surface->getBasePtr(rect.left, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + y++; + } + return in; +} + +byte* MoviePlayer::blit1(Common::Rect rect, byte *in, Graphics::Surface *surface) { + int16 y = rect.top; + int16 x = rect.left; + while (y < rect.bottom) { + + byte b = *in; + in++; + if (b & 0x80) { + if ((b & 0x40) == 0) { + int count = (b & 0x3f) + 1; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x += count; + } else { + if ((b & 0x3f) == 0) + x = rect.right; + else { + if ((b & 0x3f) != 1) { + int count = (b & 0x3f) + 1; + byte val = *in; + in++; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += val; + out++; + } + x += count; + } else { + int count = rect.right - x; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x = rect.right; + } + } + } + } else { + x += b + 1; + } + + if (x >= rect.right) { + y++; + x = rect.left; + } + } + return in; +} + +byte* MoviePlayer::blit2(Common::Rect rect, byte *in, Graphics::Surface *surface) { + int16 y = rect.top; + int16 x = rect.left; + while (y < rect.bottom) { + + byte b = *in; + in++; + if (b & 0x80) { + if ((b & 0x40) == 0) { + x += (b & 0x3f) + 1; + } else { + int count = (b & 0x3f) + 1; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x += count; + } + } else if (b == 0) { + x += b + 1; + } else if (b != 1) { + int count = b + 1; + byte val = *in; + in++; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += val; + out++; + } + x += count; + } else { + int count = rect.right - x; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x = rect.right; + } + + if (x >= rect.right) { + y++; + x = rect.left; + } + } + return in; +} + +byte* MoviePlayer::blit3(Common::Rect rect, byte *in, Graphics::Surface *surface) { + int16 y = rect.top; + int16 x = rect.left; + while (y < rect.bottom) { + + byte b = *in; + in++; + if (b & 0x80) { + if ((b & 0x40) == 0) { + x += (b & 0x3f) + 1; + } else { + if ((b & 0x3f) == 0) + x = rect.right; + else { + if ((b & 0x3f) != 1) { + int count = (b & 0x3f) + 1; + byte val = *in; + in++; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += val; + out++; + } + x += count; + } else { + int count = rect.right - x; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x = rect.right; + } + } + } + } else { + int count = b + 1; + byte *out = (byte *)surface->getBasePtr(x, y); + for (int i = 0; i < count; i++) { + *out += *in; + in++; + out++; + } + x += count; + } + + if (x >= rect.right) { + y++; + x = rect.left; + } + } + return in; +} + + +uint8 MoviePlayer::processMessages(bool keepAct, uint32 *msecs) { + if (!keepAct) + _messageProc->_act2 = ACT_NONE; + + Common::Event e; + while (g_system->getEventManager()->pollEvent(e)) { + if (e.type == Common::EVENT_QUIT) { + //_errMessage = 1; + return ACT2_83; + } + _messageProc->processMessage(e); + } + + uint8 act = _messageProc->_act2; + _messageProc->_act2 = ACT_NONE; + *msecs = g_system->getMillis(); + return act; +} + +} \ No newline at end of file diff --git a/movie.h b/movie.h new file mode 100644 index 0000000..9e70e68 --- /dev/null +++ b/movie.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef GAMOS_MOVIE_H +#define GAMOS_MOVIE_H + +#include "common/file.h" + +namespace Gamos +{ + +class GamosEngine; + +class MoviePlayer { + public: + + bool playMovie(Common::File *file, uint32 offset, GamosEngine *gamos); + + + private: + + bool init(Common::File *file, uint32 offset, GamosEngine *gamos); + bool deinit(); + bool error(); + + int processControlChunk(); + int processImageChunk(); + int processPaletteChunk(); + int processSoundChunk(); + int proccessMidiChunk(); + + bool readHdr(); + bool readCompressed(int32_t count, Common::Array *buf); + + uint8 processMessages(bool keepAct, uint32 *msecs); + + static byte* blit0(Common::Rect rect, byte *in, Graphics::Surface *surface); + static byte* blit1(Common::Rect rect, byte *in, Graphics::Surface *surface); + static byte* blit2(Common::Rect rect, byte *in, Graphics::Surface *surface); + static byte* blit3(Common::Rect rect, byte *in, Graphics::Surface *surface); + + + + private: + + GamosEngine *_gamos = nullptr; + Graphics::Screen *_screen = nullptr; + SystemProc *_messageProc = nullptr; + + + bool _doUpdateScreen = false; + uint32 _skippedFrames = 0; + uint32 _currentFrame = 0; + uint32 _firstFrameTime = 0; + + bool _forceStopMidi = false; + + int _loopCount = 1; + int _loopPoint = 0; + + Common::Point _pos; /* Movie frame leftup corner */ + Common::Point _frameSize; /* Sizes of movie frame */ + + int _midiBufferSize = 0; + int _soundBufferSize = 0; + int _paletteBufferSize = 0; + int _bufferSize = 0; + int _packedBufferSize = 0; + int _frameTime = 0; + + Common::Array _midiBuffer; + Common::Array _soundBuffer; + Common::Array _paletteBuffer; + Common::Array _buffer; + Common::Array _packedBuffer; + + bool _midiStarted = false; + bool _soundPlaying = false; + + Common::File *_file = nullptr; + + byte _hdrBytes[4]; + int32_t _hdrValue1 = 0; + int32_t _hdrValue2 = 0; +}; + +} + +#endif //GAMOS_MOVIE_H \ No newline at end of file diff --git a/music.cpp b/music.cpp new file mode 100644 index 0000000..373250e --- /dev/null +++ b/music.cpp @@ -0,0 +1,185 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "common/system.h" +#include "gamos/gamos.h" + +namespace Gamos { + +MidiMusic::MidiMusic() { + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_GM); + + _driver = MidiDriver::createMidi(dev); + _driver->open(); + _driver->sendGMReset(); +} + +MidiMusic::~MidiMusic() { + if (_driver) { + _driver->stopAllNotes(true); + _driver->close(); + delete _driver; + } +} + +void MidiMusic::stopMusic() { + g_system->getTimerManager()->removeTimerProc(_timerProc); + + if (_mutex.lock()) { + _driver->stopAllNotes(true); + + _mutex.unlock(); + } +} + +void MidiMusic::playMusic(Common::Array *midiData) { + stopMusic(); + + if (!midiData || midiData->size() <= 4) + return; + + if (_mutex.lock()) { + _pMidiData.clear(); + _pMidiData.swap(*midiData); + + _dataPos = 4; + _midiDelayTicks = 1; + _midiDelayCount = 1; + + if (_pMidiData[_dataPos] == 0xf8) { + _dataPos++; + midi2low(); + _dataPos++; + } + + g_system->getTimerManager()->installTimerProc(_timerProc, 10 * 1000, this, "Gamos::Music"); + + _mutex.unlock(); + } +} + +void MidiMusic::update() { + while (true) { + if (_dataPos >= _pMidiData.size()) + return; + + uint8 b = _pMidiData[_dataPos]; + + if (b > 0x7f) { + /* only if new event type then update _midiOp */ + _midiOp = b; + _dataPos++; + } + + bool doDelay = true; + + if (_midiOp == 0xf0 || _midiOp == 0xf7) { + int16 skipLen = midi2low(); + if (skipLen >= 0) + _dataPos += skipLen; + } else if (_midiOp == 0xff) { + if (_midiDelayTicks != -1) { + _midiDelayCount = _midiDelayTicks; + _dataPos = _dataStart; + } else { + g_system->getTimerManager()->removeTimerProc(_timerProc); + _driver->stopAllNotes(true); + } + break; + } else { + uint8 param1 = _pMidiData[_dataPos]; + uint8 param2 = 0; + _dataPos++; + + bool doSend = true; + + uint8 cmd = _midiOp & 0xf0; + if (cmd != MidiDriver_BASE::MIDI_COMMAND_PROGRAM_CHANGE && + cmd != MidiDriver_BASE::MIDI_COMMAND_CHANNEL_AFTERTOUCH) { + if (cmd == MidiDriver_BASE::MIDI_COMMAND_NOTE_OFF || + cmd == MidiDriver_BASE::MIDI_COMMAND_NOTE_ON) { + if (_midiMute) + doSend = false; + } + + b = _pMidiData[_dataPos]; + _dataPos++; + + doDelay = (b & 0x80) == 0; + param2 = b & 0x7f; + } + + if (doSend) + _driver->send(_midiOp, param1, param2); + } + + if (doDelay) { + int16 ln = midi2low(); + if (ln > 0) { + _midiTimeStamp += ln * 10; + if (g_system->getMillis() < _midiTimeStamp) + break; + } + } + } +} + +int16 MidiMusic::midi2low() { + if (_dataPos >= _pMidiData.size()) + return -1; + + int16 dat = _pMidiData[_dataPos]; + _dataPos++; + + if (dat & 0x80) { + if (_dataPos >= _pMidiData.size()) + return -1; + + dat &= 0x7f; + dat |= _pMidiData[_dataPos] << 7; + + _dataPos++; + } + return dat; +} + + +void MidiMusic::_timerProc(void *data) { + if (!data) + return; + + MidiMusic *_this = (MidiMusic *)data; + + if (_this->_midiDelayCount != 0) { + _this->_midiDelayCount--; + if (_this->_midiDelayCount != 0) + return; + + _this->_midiTimeStamp = g_system->getMillis(); + } + + if (_this->_midiTimeStamp <= g_system->getMillis() && _this->_mutex.lock()) { + _this->update(); + _this->_mutex.unlock(); + } +} + +}; \ No newline at end of file diff --git a/music.h b/music.h new file mode 100644 index 0000000..68be618 --- /dev/null +++ b/music.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef GAMOS_MUSIC_H +#define GAMOS_MUSIC_H + + + +#include "audio/mididrv.h" +#include "common/memstream.h" +#include "common/mutex.h" +#include "common/scummsys.h" +#include "common/timer.h" +#include "common/system.h" + +namespace Gamos { + +class MidiMusic { +private: + MidiDriver *_driver = nullptr; + Common::Array _pMidiData; + + Common::Mutex _mutex; + + uint32 _dataPos = 0; + uint32 _dataStart = 0; + int32 _midiDelayTicks = 0; + int32 _midiDelayCount = 0; + uint32 _midiTimeStamp = 0; + uint32 _midiOp = 0; /* save midi event type between update cycles */ + bool _midiMute = false; + +public: + + MidiMusic(); + ~MidiMusic(); + + void stopMusic(); + void playMusic(Common::Array *midiData); + void update(); + int16 midi2low(); + + static void _timerProc(void *data); +}; + +}; + +#endif //GAMOS_MUSIC_H \ No newline at end of file diff --git a/proc.cpp b/proc.cpp new file mode 100644 index 0000000..c095bc9 --- /dev/null +++ b/proc.cpp @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "gamos/gamos.h" + +namespace Gamos { + +void SystemProc::processMessage(const Common::Event &ev) { + switch(ev.type) { + case Common::EVENT_KEYDOWN: + if ((_gd2flags & 1) == 0) + return; + + if (ev.kbd.keycode == _keyCodes[0]) + _act1 = 0; + else if (ev.kbd.keycode == _keyCodes[1]) + _act1 = 1; + else if (ev.kbd.keycode == _keyCodes[2]) + _act1 = 2; + else if (ev.kbd.keycode == _keyCodes[3]) + _act1 = 3; + else if (ev.kbd.keycode == _keyCodes[4]) + _act1 = 4; + else if (ev.kbd.keycode == _keyCodes[5]) + _act1 = 5; + else if (ev.kbd.keycode == _keyCodes[6]) + _act1 = 6; + else if (ev.kbd.keycode == _keyCodes[7]) + _act1 = 7; + else { + if (ev.kbd.keycode == _keyCodes[8]) + _act2 = ACT2_82; + else if (ev.kbd.keycode == _keyCodes[9]) + _act2 = ACT2_83; + else if (ev.kbd.keycode == _keyCodes[10]) + _act2 = ACT2_8f; + else if (ev.kbd.keycode == _keyCodes[11]) + _act2 = ACT2_84; + + return; + } + + if ((_act1 < 8) && (ev.kbd.flags & Common::KBD_SHIFT)) + _act1 |= 8; + + _mouseAct = _mouseReported; + break; + + case Common::EVENT_MOUSEMOVE: + if ((_gd2flags & 1) == 0) + return; + + _mouseReported = ev.mouse; + + break; + + default: + break; + } +} + +} \ No newline at end of file diff --git a/proc.h b/proc.h new file mode 100644 index 0000000..a691b3e --- /dev/null +++ b/proc.h @@ -0,0 +1,50 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef GAMOS_PROC_H +#define GAMOS_PROC_H + +#include + +namespace Gamos { + +class SystemProc { +public: + + void processMessage(const Common::Event &ev); + +public: + uint8 _act1 = 0; + uint8 _act2 = 0; + + Common::Point _mouseReported; + Common::Point _mouseAct; + + uint8 _gd2flags = 0; /* 0x4 */ + uint16 _keyCodes[12]; /* 0x40 */ + +}; + + +} + +#endif //GAMOS_PROC_H \ No newline at end of file