Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

1
engines/awe/POTFILES Normal file
View File

@@ -0,0 +1 @@
engines/awe/metaengine.cpp

139
engines/awe/aifc_player.cpp Normal file
View File

@@ -0,0 +1,139 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/aifc_player.h"
#include "awe/detection.h"
namespace Awe {
static uint32 READ_IEEE754(const uint8 *p) {
const uint32 m = READ_BE_UINT32(p + 2);
const int exp = 30 - p[1];
return (m >> exp);
}
AifcPlayer::AifcPlayer() {
}
bool AifcPlayer::play(int mixRate, const char *path, uint32 startOffset) {
_ssndSize = 0;
if (_f.open(path)) {
_f.seek(startOffset);
uint8 buf[12];
_f.read(buf, sizeof(buf));
if (memcmp(buf, "FORM", 4) == 0 && memcmp(buf + 8, "AIFC", 4) == 0) {
const uint32 size = READ_BE_UINT32(buf + 4);
for (uint32 offset = 12; offset + 8 < size; ) {
_f.seek(startOffset + offset);
_f.read(buf, 8);
const uint32 sz = READ_BE_UINT32(buf + 4);
if (memcmp(buf, "COMM", 4) == 0) {
const int channels = _f.readUint16BE();
_f.readUint32BE(); // samples per frame
const int bits = _f.readUint16BE();
_f.read(buf, 10);
const int rate = READ_IEEE754(buf);
if (channels != 2) {
warning("Unsupported AIFF-C channels %d rate %d (%s)", channels, rate, path);
break;
}
_f.read(buf, 4);
if (memcmp(buf, "SDX2", 4) != 0) {
warning("Unsupported compression");
break;
}
debugC(kDebugSound, "AIFF-C channels %d rate %d bits %d", channels, rate, bits);
_rate.reset(rate, mixRate);
} else if (memcmp(buf, "SSND", 4) == 0) {
_f.readUint32BE(); // block offset
_f.readUint32BE(); // block size
_ssndOffset = startOffset + offset + 8 + 8;
_ssndSize = sz;
debugC(kDebugSound, "AIFF-C ssnd size %d", _ssndSize);
break;
} else if (memcmp(buf, "FVER", 4) == 0) {
const uint32 version = _f.readUint32BE();
if (version != 0xA2805140) {
warning("Unexpected AIFF-C version 0x%x (%s)", version, path);
}
} else if (memcmp(buf, "INST", 4) == 0) {
// unused
} else if (memcmp(buf, "MARK", 4) == 0) {
const int count = _f.readUint16BE();
for (int i = 0; i < count; ++i) {
_f.readUint16BE(); // marker_id
_f.readUint32BE(); // marker_position
const int len = _f.readByte();
if (len != 0) {
char name[256];
_f.read(name, len);
name[len] = 0;
}
// pad ((len + 1) & 1)
}
} else {
warning("Unhandled AIFF-C tag '%02x%02x%02x%02x' size %d offset 0x%x path %s", buf[0], buf[1], buf[2], buf[3], sz, startOffset + offset, path);
break;
}
offset += sz + 8;
}
}
}
_pos = 0;
_sampleL = _sampleR = 0;
return _ssndSize != 0;
}
void AifcPlayer::stop() {
_f.close();
}
static int16 decodeSDX2(int16 prev, int8 data) {
const int sqr = data * ABS(data) * 2;
return (data & 1) != 0 ? prev + sqr : sqr;
}
int8 AifcPlayer::readSampleData() {
if (_pos >= _ssndSize) {
_pos = 0;
_f.seek(_ssndOffset);
}
const int8 data = _f.readByte();
++_pos;
return data;
}
void AifcPlayer::decodeSamples() {
for (const uint32 pos = _rate.getInt(); pos == _rate.getInt(); _rate.offset += _rate.inc) {
_sampleL = decodeSDX2(_sampleL, readSampleData());
_sampleR = decodeSDX2(_sampleR, readSampleData());
}
}
void AifcPlayer::readSamples(int16 *buf, int len) {
for (int i = 0; i < len; i += 2) {
decodeSamples();
*buf++ = _sampleL;
*buf++ = _sampleR;
}
}
} // namespace Awe

51
engines/awe/aifc_player.h Normal file
View File

@@ -0,0 +1,51 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_AIFC_PLAYER_H
#define AWE_AIFC_PLAYER_H
#include "common/file.h"
#include "awe/intern.h"
namespace Awe {
struct AifcPlayer {
Common::File _f;
uint32 _ssndOffset = 0;
uint32 _ssndSize = 0;
uint32 _pos = 0;
int16 _sampleL = 0, _sampleR = 0;
Frac _rate;
AifcPlayer();
bool play(int mixRate, const char *path, uint32 offset);
void stop();
int8 readSampleData();
void decodeSamples();
void readSamples(int16 *buf, int len);
};
} // namespace Awe
#endif

221
engines/awe/awe.cpp Normal file
View File

@@ -0,0 +1,221 @@
/* ScummVM - Graphic Adventure AweEngine
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "audio/mixer.h"
#include "common/config-manager.h"
#include "common/rendermode.h"
#include "common/stream.h"
#include "awe/awe.h"
#include "awe/engine.h"
#include "awe/gfx.h"
#include "awe/resource.h"
#include "awe/script.h"
#include "awe/system_stub.h"
#include "awe/video.h"
namespace Awe {
AweEngine *g_engine;
/* unused
static const struct {
const char *name;
int type;
} GRAPHICS[] = {
{ "original", GRAPHICS_ORIGINAL },
{ "software", GRAPHICS_SOFTWARE },
{ "gl", GRAPHICS_GL },
{ nullptr, -1 }
};
static const struct {
const char *name;
int difficulty;
} DIFFICULTIES[] = {
{ "easy", DIFFICULTY_EASY },
{ "normal", DIFFICULTY_NORMAL },
{ "hard", DIFFICULTY_HARD },
{ nullptr, -1 }
};
*/
bool Gfx::_is1991;
Graphics::PixelFormat Gfx::_format;
bool Video::_useEGA;
Difficulty Script::_difficulty;
bool Script::_useRemasteredAudio;
static Gfx *createGraphics(int type) {
switch (type) {
case GRAPHICS_ORIGINAL:
Gfx::_is1991 = true;
// fall-through
case GRAPHICS_SOFTWARE:
debugC(kDebugInfo, "Using software graphics");
return GraphicsSoft_create();
case GRAPHICS_GL:
debugC(kDebugInfo, "Using GL graphics");
#ifdef USE_GL
return GraphicsGL_create();
#else
break;
#endif
default:
break;
}
return nullptr;
}
static int getGraphicsType(DataType type) {
switch (type) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
case DT_3DO:
return GRAPHICS_GL;
default:
return GRAPHICS_ORIGINAL;
}
}
AweEngine::AweEngine(OSystem *syst, const Awe::AweGameDescription *gameDesc)
: Engine(syst), _gameDescription(gameDesc),
_random("Awe") {
g_engine = this;
Gfx::_is1991 = false;
Gfx::_format = Graphics::PixelFormat::createFormatCLUT8();
if (ConfMan.hasKey("render_mode") && !ConfMan.get("render_mode").empty())
Video::_useEGA = (Common::parseRenderMode(ConfMan.get("render_mode")) == Common::kRenderEGA) ? true : false;
else
Video::_useEGA = false;
Script::_difficulty = DIFFICULTY_NORMAL;
Script::_useRemasteredAudio = true;
}
AweEngine::~AweEngine() {
}
Common::Error AweEngine::run() {
// Setup mixer
if (!_mixer->isReady()) {
warning("Sound initialization failed.");
}
const int part = 16001;
const Language lang = getLanguage();
const DataType dataType = getDataType();
int graphicsType = GRAPHICS_SOFTWARE;
DisplayMode dm;
dm.mode = DisplayMode::WINDOWED;
dm.width = GFX_W;
dm.height = GFX_H;
dm.opengl = (graphicsType == GRAPHICS_GL);
const bool defaultGraphics = true;
const bool demo3JoyInputs = false;
Sound snd(_mixer);
Awe::Engine *e = new Awe::Engine(&snd, dataType, part);
if (defaultGraphics) {
// if not set, use original software graphics for 199x editions and GL for the anniversary and 3DO versions
graphicsType = getGraphicsType(dataType);
dm.opengl = (graphicsType == GRAPHICS_GL);
}
if (graphicsType != GRAPHICS_GL && dataType == DT_3DO) {
graphicsType = GRAPHICS_SOFTWARE;
// TODO: Select the best pixel format at runtime
Gfx::_format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
}
Gfx *graphics = createGraphics(graphicsType);
if (!graphics)
error("The Anniversary versions graphics is not currently supported");
if (dataType == DT_20TH_EDITION) {
switch (Script::_difficulty) {
case DIFFICULTY_EASY:
debugC(kDebugInfo, "Using easy difficulty");
break;
case DIFFICULTY_NORMAL:
debugC(kDebugInfo, "Using normal difficulty");
break;
case DIFFICULTY_HARD:
debugC(kDebugInfo, "Using hard difficulty");
break;
}
}
if (dataType == DT_15TH_EDITION || dataType == DT_20TH_EDITION) {
if (Script::_useRemasteredAudio) {
debugC(kDebugInfo, "Using remastered audio");
} else {
debugC(kDebugInfo, "Using original audio");
}
}
SystemStub *stub = SystemStub_create(
dataType == DT_15TH_EDITION ||
dataType == DT_20TH_EDITION
);
stub->init(dm);
e->setSystemStub(stub, graphics);
if (demo3JoyInputs && dataType == DT_DOS) {
e->_res.readDemo3Joy();
}
e->_res._copyProtection = ConfMan.getBool("copy_protection");
e->setup(lang, graphicsType);
while (!stub->_pi.quit) {
e->run();
}
_mixer->stopAll();
e->finish();
delete e;
stub->fini();
delete stub;
delete graphics;
return Common::kNoError;
}
DataType AweEngine::getDataType() const {
return (DataType)_gameDescription->_gameType;
}
Common::Language AweEngine::getLanguage() const {
return _gameDescription->desc.language;
}
} // namespace Awe

65
engines/awe/awe.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_AWE_H
#define AWE_AWE_H
#include "common/scummsys.h"
#include "common/random.h"
#include "engines/engine.h"
#include "awe/detection.h"
namespace Awe {
class AweEngine : public ::Engine {
private:
const AweGameDescription *_gameDescription;
Common::RandomSource _random;
public:
AweEngine(OSystem *syst, const AweGameDescription *gameDesc);
~AweEngine() override;
Common::Error run() override;
#ifdef TODO
bool hasFeature(EngineFeature f) const override;
bool isDemo() const;
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
#endif
int getRandomNumber(int max) {
return _random.getRandomNumber(max);
}
int getRandomNumber(int min, int max) {
return min + _random.getRandomNumber(max - min);
}
DataType getDataType() const;
Common::Language getLanguage() const;
};
extern AweEngine *g_engine;
} // namespace Awe
#endif

92
engines/awe/bitmap.cpp Normal file
View File

@@ -0,0 +1,92 @@
/* ScummVM - Graphic Adventure AweEngine
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "awe/bitmap.h"
namespace Awe {
static void clut(const uint8 *src, const uint8 *pal, int pitch, int w, int h, int bpp, bool flipY, int colorKey, uint8 *dst) {
int dstPitch = bpp * w;
if (flipY) {
dst += (h - 1) * bpp * w;
dstPitch = -bpp * w;
}
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
const int color = src[x];
const int b = pal[color * 4];
const int g = pal[color * 4 + 1];
const int r = pal[color * 4 + 2];
dst[x * bpp] = r;
dst[x * bpp + 1] = g;
dst[x * bpp + 2] = b;
if (bpp == 4) {
dst[x * bpp + 3] = (color == 0 || (colorKey == ((r << 16) | (g << 8) | b))) ? 0 : 255;
}
}
src += w;
dst += dstPitch;
}
}
uint8 *decode_bitmap(const uint8 *src, bool alpha, int colorKey, int *w, int *h) {
if (memcmp(src, "BM", 2) != 0) {
return nullptr;
}
const uint32 imageOffset = READ_LE_UINT32(src + 0xA);
const int width = READ_LE_UINT32(src + 0x12);
const int height = READ_LE_UINT32(src + 0x16);
const int depth = READ_LE_UINT16(src + 0x1C);
const int compression = READ_LE_UINT32(src + 0x1E);
if ((depth != 8 && depth != 32) || compression != 0) {
warning("Unhandled bitmap depth %d compression %d", depth, compression);
return nullptr;
}
const int bpp = (!alpha && colorKey < 0) ? 3 : 4;
uint8 *dst = (uint8 *)malloc(width * height * bpp);
if (!dst) {
warning("Failed to allocate bitmap buffer, width %d height %d bpp %d", width, height, bpp);
return nullptr;
}
if (depth == 8) {
const uint8 *palette = src + 14 /* BITMAPFILEHEADER */ + 40 /* BITMAPINFOHEADER */;
const bool flipY = true;
clut(src + imageOffset, palette, (width + 3) & ~3, width, height, bpp, flipY, colorKey, dst);
} else {
assert(depth == 32 && bpp == 3);
const uint8 *p = src + imageOffset;
for (int y = height - 1; y >= 0; --y) {
uint8 *q = dst + y * width * bpp;
for (int x = 0; x < width; ++x) {
const uint32 color = READ_LE_UINT32(p); p += 4;
*q++ = (color >> 16) & 255;
*q++ = (color >> 8) & 255;
*q++ = color & 255;
}
}
}
*w = width;
*h = height;
return dst;
}
} // namespace Awe

33
engines/awe/bitmap.h Normal file
View File

@@ -0,0 +1,33 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_BITMAP_H
#define AWE_BITMAP_H
#include "intern.h"
namespace Awe {
uint8 *decode_bitmap(const uint8 *src, bool alpha, int colorKey, int *w, int *h);
} // namespace Awe
#endif

View File

@@ -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 awe "Another World" yes "" "" "" ""

4
engines/awe/credits.pl Normal file
View File

@@ -0,0 +1,4 @@
begin_section("awe");
add_person("Gregory Montoir", "cyx", "");
add_person("Paul Gilbert", "dreammaster", "");
end_section();

45
engines/awe/detection.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/util.h"
#include "awe/detection.h"
#include "awe/detection_tables.h"
const DebugChannelDef AweMetaEngineDetection::debugFlagList[] = {
{ Awe::kDebugScript, "Scripts", "Scripts debug level" },
{ Awe::kDebugBank, "Bank", "Bank debug level" },
{ Awe::kDebugVideo, "Video", "Video debug level" },
{ Awe::kDebugSound, "Sound", "Sound debug level" },
{ Awe::kDebugInfo, "Info", "Info debug level" },
{ Awe::kDebugPak, "Pak", "Pak debug level" },
{ Awe::kDebugResource, "Resource", "Resource debug level" },
DEBUG_CHANNEL_END
};
AweMetaEngineDetection::AweMetaEngineDetection() : AdvancedMetaEngineDetection(
Awe::gameDescriptions, Awe::aweGames) {
_flags = kADFlagMatchFullPaths;
}
REGISTER_PLUGIN_STATIC(AWE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AweMetaEngineDetection);

90
engines/awe/detection.h Normal file
View File

@@ -0,0 +1,90 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_DETECTION_H
#define AWE_DETECTION_H
#include "engines/advancedDetector.h"
namespace Awe {
enum AweDebugChannels {
kDebugScript = 1,
kDebugBank,
kDebugVideo,
kDebugSound,
kDebugInfo,
kDebugPak,
kDebugResource
};
enum DataType {
DT_DOS,
DT_AMIGA,
DT_ATARI,
DT_15TH_EDITION,
DT_20TH_EDITION,
DT_WIN31,
DT_3DO,
DT_ATARI_DEMO // ST Action Issue44 Disk28
};
struct AweGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int _gameType;
};
extern const PlainGameDescriptor aweGames[];
extern const AweGameDescription gameDescriptions[];
#define GAMEOPTION_COPY_PROTECTION GUIO_GAMEOPTIONS1
} // namespace Awe
class AweMetaEngineDetection : public AdvancedMetaEngineDetection<Awe::AweGameDescription> {
static const DebugChannelDef debugFlagList[];
public:
AweMetaEngineDetection();
~AweMetaEngineDetection() override {}
const char *getName() const override {
return "awe";
}
const char *getEngineName() const override {
return "Awe";
}
const char *getOriginalCopyright() const override {
return "Out of This World (C) 1991 by Delphine Software International";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
#endif

View File

@@ -0,0 +1,240 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
namespace Awe {
const PlainGameDescriptor aweGames[] = {
{ "anotherworld", "Another World - Out of this World" },
{ nullptr, nullptr }
};
const AweGameDescription gameDescriptions[] = {
// DOS Floppy (1991) French/English
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("memlist.bin", "f2bf61fe20c98108b2256e96d57d3fe0", 2940),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("memlist.bin", "f2bf61fe20c98108b2256e96d57d3fe0", 2940),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
// DOS Floppy - ANOTHER WORLD PC (19/3/92)
// This version is also included on the Delphine Classic Collection CD
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("memlist.bin", "63675fdb53dd4a2b8d72182bbee47592", 2940),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
// Out of This World, Valueware and Interplay's 10 Year Anthology
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("memlist.bin", "4605962431175a5b961f6db4041adcff", 2940),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
// Out of This World (alternate)
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("memlist.bin", "ee9e67fcaace0300fec4e619299e6e0e", 2940),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
{
{
"anotherworld",
"Demo",
AD_ENTRY1s("memlist.bin", "11e0cc58aeb47ad1dfc1c4dae8dcd9ee", 2940),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO3(GUIO_RENDEREGA, GUIO_RENDERVGA, GAMEOPTION_COPY_PROTECTION)
},
DT_DOS
},
{
{
"anotherworld",
nullptr,
AD_ENTRY1s("bank", "30fb99cb4cbd812273d0b54b7b4a18ca", 987462),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
DT_WIN31
},
{
{
"anotherworld",
"20th Anniversary",
AD_ENTRY1s("game/dat/file017.dat", "3c3e73a77d2cab00324c1affd3f2ef8c", 25108),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
GUIO1(GUIO_NONE)
},
DT_20TH_EDITION
},
// European version
// Also included as a bonus on both GOG and Steam.
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244868,
"bank03", "2ef3440fd6205634b257d56b0bc3ea51", 127846),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244868,
"bank03", "2ef3440fd6205634b257d56b0bc3ea51", 127846),
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244868,
"bank03", "2ef3440fd6205634b257d56b0bc3ea51", 127846),
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
// Out of this World (USA)
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244822,
"bank03", "b8cdbcd57e9953423a21dadeeca3cdfa", 128040),
Common::EN_USA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
// Delphine Classic Collection (Kixx)
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244674,
"bank03", "c2594927418291119c2996eabcf6d0f1", 128038),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244674,
"bank03", "c2594927418291119c2996eabcf6d0f1", 128038),
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
{
{
"anotherworld",
nullptr,
AD_ENTRY2s("bank01", "8cec5badf5bea89bff3a550daff79861", 244674,
"bank03", "c2594927418291119c2996eabcf6d0f1", 128038),
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_UNSTABLE,
GUIO1(GAMEOPTION_COPY_PROTECTION)
},
DT_AMIGA
},
{ AD_TABLE_END_MARKER, 0 }
};
} // End of namespace Awe

264
engines/awe/engine.cpp Normal file
View File

@@ -0,0 +1,264 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/engine.h"
#include "awe/gfx.h"
#include "awe/resource_nth.h"
#include "awe/system_stub.h"
namespace Awe {
static const uint16 RESTART_POS[36 * 2] = {
16008, 0, 16001, 0, 16002, 10, 16002, 12, 16002, 14,
16003, 20, 16003, 24, 16003, 26, 16004, 30, 16004, 31,
16004, 32, 16004, 33, 16004, 34, 16004, 35, 16004, 36,
16004, 37, 16004, 38, 16004, 39, 16004, 40, 16004, 41,
16004, 42, 16004, 43, 16004, 44, 16004, 45, 16004, 46,
16004, 47, 16004, 48, 16004, 49, 16006, 64, 16006, 65,
16006, 66, 16006, 67, 16006, 68, 16005, 50, 16006, 60,
16007, 0
};
Engine::Engine(Sound *sound, DataType dataType, int partNum) :
_sound(sound), _script(sound, &_res, &_ply, &_vid),
_res(&_vid, dataType), _ply(&_res), _vid(&_res),
_partNum(partNum) {
sound->setPlayer(&_ply);
}
void Engine::setSystemStub(SystemStub *stub, Gfx *graphics) {
_stub = stub;
_script._stub = stub;
_graphics = graphics;
}
void Engine::run() {
switch (_state) {
case kStateLogo3DO:
doThreeScreens();
scrollText(0, 380, Video::NOTE_TEXT_3DO);
playCinepak("Logo.Cine");
playCinepak("Spintitle.Cine");
break;
case kStateTitle3DO:
titlePage();
break;
case kStateEnd3DO:
doEndCredits();
break;
case kStateGame:
_script.setupTasks();
_script.updateInput();
processInput();
_script.runTasks();
#if 0
_mix.update();
#endif
if (_res.getDataType() == DT_3DO) {
switch (_res._nextPart) {
case 16009:
_state = kStateEnd3DO;
break;
case 16000:
_state = kStateTitle3DO;
break;
default:
break;
}
}
break;
default:
break;
}
}
void Engine::setup(Language lang, int graphicsType) {
_vid._graphics = _graphics;
int w = GFX_W;
int h = GFX_H;
if (_res.getDataType() != DT_3DO) {
_vid._graphics->_fixUpPalette = FIXUP_PALETTE_REDRAW;
}
_vid.init();
_res._lang = lang;
_res.allocMemBlock();
_res.readEntries();
_res.dumpEntries();
const bool isNth = !Gfx::_is1991 && (_res.getDataType() == DT_15TH_EDITION || _res.getDataType() == DT_20TH_EDITION);
if (isNth) {
// get HD background bitmaps resolution
_res._nth->getBitmapSize(&w, &h);
}
_graphics->init(w, h);
if (isNth) {
_res.loadFont();
_res.loadHeads();
} else {
_vid.setDefaultFont();
}
_script.init();
switch (_res.getDataType()) {
case DT_DOS:
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
switch (lang) {
case Common::FR_FRA:
_vid._stringsTable = Video::STRINGS_TABLE_FR;
break;
case Common::IT_ITA:
_vid._stringsTable = Video::STRINGS_TABLE_ITA;
break;
case Common::EN_ANY:
default:
_vid._stringsTable = Video::STRINGS_TABLE_ENG;
break;
}
break;
case DT_WIN31:
case DT_15TH_EDITION:
case DT_20TH_EDITION:
case DT_3DO:
break;
}
if (_res._copyProtection) {
switch (_res.getDataType()) {
case DT_DOS:
if (!_res._hasPasswordScreen) {
break;
}
/* fall-through */
case DT_AMIGA:
case DT_ATARI:
case DT_WIN31:
_partNum = kPartCopyProtection;
break;
default:
break;
}
}
if (_res.getDataType() == DT_3DO && _partNum == kPartIntro) {
_state = kStateLogo3DO;
} else {
_state = kStateGame;
const int num = _partNum;
if (num < 36) {
_script.restartAt(RESTART_POS[num * 2], RESTART_POS[num * 2 + 1]);
} else {
_script.restartAt(num);
}
}
}
void Engine::finish() {
_graphics->fini();
_ply.stop();
_sound->stopAll();
_res.freeMemBlock();
}
void Engine::processInput() {
if (_stub->_pi.fastMode) {
_script._fastMode = !_script._fastMode;
_stub->_pi.fastMode = false;
}
}
void Engine::doThreeScreens() {
_script.snd_playMusic(1, 0, 0);
static const int BITMAPS[] = { 67, 68, 69, -1 };
for (int i = 0; BITMAPS[i] != -1 && !_stub->_pi.quit; ++i) {
_res.loadBmp(BITMAPS[i]);
_vid.updateDisplay(0, _stub);
while (!_stub->_pi.quit) {
_stub->processEvents();
if (_stub->_pi.action) {
_stub->_pi.action = false;
break;
}
_stub->sleep(50);
}
}
_state = kStateTitle3DO;
}
void Engine::doEndCredits() {
scrollText(0, 380, Video::END_TEXT_3DO);
_script.snd_playMusic(0, 0, 0);
playCinepak("ootw2.cine");
_state = kStateTitle3DO;
}
void Engine::playCinepak(const char *name) {
warning("STUB: Engine::playCinepak()");
}
void Engine::scrollText(int a, int b, const char *text) {
warning("STUB: Engine::scrollText()");
}
void Engine::titlePage() {
_res.loadBmp(70);
static const int kCursorColor = 0;
_vid.setPaletteColor(kCursorColor, 255, 0, 0);
static const uint16 YPOS[] = { 97, 123, 149 };
int y = 0;
while (!_stub->_pi.quit) {
_vid.copyPage(0, 1, 0);
_vid.drawRect(1, kCursorColor, 97, YPOS[y], 210, YPOS[y + 1]);
_stub->processEvents();
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
_partNum = kPartPassword;
y = 1;
}
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
_partNum = kPartIntro;
y = 0;
}
if (_stub->_pi.action) {
_stub->_pi.action = false;
_script.restartAt(_partNum);
break;
}
_vid.updateDisplay(1, _stub);
_stub->sleep(50);
}
_state = kStateGame;
}
void Engine::saveGameState(uint8 slot, const char *desc) {
warning("STUB: Engine::saveGameState()");
}
void Engine::loadGameState(uint8 slot) {
warning("STUB: Engine::loadGameState()");
}
} // namespace Awe

83
engines/awe/engine.h Normal file
View File

@@ -0,0 +1,83 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_ENGINE_H
#define AWE_ENGINE_H
#include "audio/mixer.h"
#include "awe/intern.h"
#include "awe/resource.h"
#include "awe/script.h"
#include "awe/sfx_player.h"
#include "awe/sound.h"
#include "awe/video.h"
namespace Awe {
struct Gfx;
struct SystemStub;
struct Engine {
enum {
kStateLogo3DO,
kStateTitle3DO,
kStateEnd3DO,
kStateGame
};
int _state = 0;
Gfx *_graphics = nullptr;
SystemStub *_stub = nullptr;
Script _script;
Sound *_sound = nullptr;
Resource _res;
SfxPlayer _ply;
Video _vid;
int _partNum = 0;
Engine(Sound *sound, DataType dataType, int partNum);
void setSystemStub(SystemStub *, Gfx *);
const char *getGameTitle(Language lang) const {
return _res.getGameTitle(lang);
}
void run();
void setup(Language lang, int graphicsType);
void finish();
void processInput();
// 3DO
void doThreeScreens();
void doEndCredits();
void playCinepak(const char *name);
void scrollText(int a, int b, const char *text);
void titlePage();
void saveGameState(uint8 slot, const char *desc);
void loadGameState(uint8 slot);
};
} // namespace Awe
#endif

96
engines/awe/gfx.h Normal file
View File

@@ -0,0 +1,96 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_GRAPHICS_H
#define AWE_GRAPHICS_H
#include "graphics/surface.h"
#include "awe/intern.h"
namespace Awe {
enum {
FMT_CLUT,
FMT_RGB555,
FMT_RGB,
FMT_RGBA,
};
enum {
FIXUP_PALETTE_NONE,
FIXUP_PALETTE_REDRAW, // redraw all primitives on setPal script call
};
enum {
COL_ALPHA = 0x10, // transparent pixel (OR'ed with 0x8)
COL_PAGE = 0x11, // buffer 0 pixel
COL_BMP = 0xFF, // bitmap in buffer 0 pixel
};
enum {
GRAPHICS_ORIGINAL,
GRAPHICS_SOFTWARE,
GRAPHICS_GL
};
enum {
ALPHA_COLOR_INDEX = 12, // 3DO uses 0x0010 (RGB555) as the blend color, using color #12 matches Amiga/DOS graphics better
GFX_W = 320,
GFX_H = 200
};
struct SystemStub;
struct Gfx {
static const uint8 FONT[];
static bool _is1991; // draw graphics as in the original 1991 game release
static Graphics::PixelFormat _format;
static const uint16 SHAPES_MASK_OFFSET[];
static const int SHAPES_MASK_COUNT;
static const uint8 SHAPES_MASK_DATA[];
int _fixUpPalette = 0;
virtual ~Gfx() {};
virtual void init(int targetW, int targetH) {}
virtual void fini() {}
virtual void setFont(const uint8 *src, int w, int h) = 0;
virtual void setPalette(const Color *colors, int count) = 0;
virtual void setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) = 0;
virtual void drawSprite(int buffer, int num, const Point *pt, uint8 color) = 0;
virtual void drawBitmap(int buffer, const uint8 *data, int w, int h, int fmt) = 0;
virtual void drawPoint(int buffer, uint8 color, const Point *pt) = 0;
virtual void drawQuadStrip(int buffer, uint8 color, const QuadStrip *qs) = 0;
virtual void drawStringChar(int buffer, uint8 color, char c, const Point *pt) = 0;
virtual void clearBuffer(int num, uint8 color) = 0;
virtual void copyBuffer(int dst, int src, int vscroll = 0) = 0;
virtual void drawBuffer(int num, SystemStub *) = 0;
virtual void drawRect(int num, uint8 color, const Point *pt, int w, int h) = 0;
virtual void drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) = 0;
};
Gfx *GraphicsSoft_create();
} // namespace Awe
#endif

839
engines/awe/graphics_gl.cpp Normal file
View File

@@ -0,0 +1,839 @@
/* ScummVM - Graphic Adventure AweEngine
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/gfx.h"
#include "awe/system_stub.h"
namespace Awe {
#ifdef TODO
static struct {
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
} _fptr;
static void setupFboFuncs() {
#ifdef _WIN32
_fptr.glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBindFramebuffer");
_fptr.glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)SDL_GL_GetProcAddress("glGenFramebuffers");
_fptr.glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)SDL_GL_GetProcAddress("glFramebufferTexture2D");
_fptr.glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)SDL_GL_GetProcAddress("glCheckFramebufferStatus");
#else
_fptr.glBindFramebuffer = glBindFramebuffer;
_fptr.glGenFramebuffers = glGenFramebuffers;
_fptr.glFramebufferTexture2D = glFramebufferTexture2D;
_fptr.glCheckFramebufferStatus = glCheckFramebufferStatus;
#endif
}
static GLuint kNoTextureId = (GLuint)-1;
static bool hasExtension(const char *exts, const char *name) {
const char *p = strstr(exts, name);
if (p) {
p += strlen(name);
return *p == ' ' || *p == 0;
}
return false;
}
static int roundPow2(int sz) {
if (sz != 0 && (sz & (sz - 1)) == 0) {
return sz;
}
int textureSize = 1;
while (textureSize < sz) {
textureSize <<= 1;
}
return textureSize;
}
struct Texture {
bool _npotTex;
GLuint _id;
int _w, _h;
float _u, _v;
uint8 *_rgbData;
const uint8 *_raw16Data;
int _fmt;
void init();
void uploadDataCLUT(const uint8 *data, int srcPitch, int w, int h, const Color *pal);
void uploadDataRGB(const void *data, int srcPitch, int w, int h, int fmt, int type);
void draw(int w, int h);
void clear();
void readRaw16(const uint8 *src, const Color *pal, int w, int h);
void readFont(const uint8 *src);
void readRGB555(const uint16 *src, int w, int h);
};
void Texture::init() {
_npotTex = false;
_id = kNoTextureId;
_w = _h = 0;
_u = _v = 0.f;
_rgbData = 0;
_raw16Data = 0;
_fmt = -1;
}
static void convertTextureCLUT(const uint8 *src, const int srcPitch, int w, int h, uint8 *dst, int dstPitch, const Color *pal, bool alpha) {
for (int y = 0; y < h; ++y) {
int offset = 0;
for (int x = 0; x < w; ++x) {
const uint8 color = src[x];
dst[offset++] = pal[color].r;
dst[offset++] = pal[color].g;
dst[offset++] = pal[color].b;
if (alpha) {
if (color == 0) {
dst[offset++] = 0;
} else {
dst[offset++] = 255;
}
}
}
dst += dstPitch;
src += srcPitch;
}
}
void Texture::uploadDataCLUT(const uint8 *data, int srcPitch, int w, int h, const Color *pal) {
if (w != _w || h != _h) {
free(_rgbData);
_rgbData = 0;
}
int depth = 1;
int fmt = GL_RGB;
int type = GL_UNSIGNED_BYTE;
switch (_fmt) {
case FMT_CLUT:
depth = 3;
fmt = GL_RGB;
break;
case FMT_RGB:
depth = 3;
fmt = GL_RGB;
break;
case FMT_RGBA:
depth = 4;
fmt = GL_RGBA;
break;
default:
return;
}
const bool alpha = (_fmt == FMT_RGBA);
if (!_rgbData) {
_w = _npotTex ? w : roundPow2(w);
_h = _npotTex ? h : roundPow2(h);
_rgbData = (uint8 *)malloc(_w * _h * depth);
if (!_rgbData) {
return;
}
_u = w / (float)_w;
_v = h / (float)_h;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &_id);
glBindTexture(GL_TEXTURE_2D, _id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (fmt == GL_RED) ? GL_NEAREST : GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (fmt == GL_RED) ? GL_NEAREST : GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
convertTextureCLUT(data, srcPitch, w, h, _rgbData, _w * depth, pal, alpha);
glTexImage2D(GL_TEXTURE_2D, 0, fmt, _w, _h, 0, fmt, type, _rgbData);
} else {
glBindTexture(GL_TEXTURE_2D, _id);
convertTextureCLUT(data, srcPitch, w, h, _rgbData, _w * depth, pal, alpha);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _w, _h, fmt, type, _rgbData);
}
}
void Texture::uploadDataRGB(const void *data, int srcPitch, int w, int h, int fmt, int type) {
_w = w;
_h = h;
_u = 1.f;
_v = 1.f;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &_id);
glBindTexture(GL_TEXTURE_2D, _id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, fmt, _w, _h, 0, fmt, type, data);
}
void Texture::draw(int w, int h) {
if (_id != kNoTextureId) {
glEnable(GL_TEXTURE_2D);
glColor4ub(255, 255, 255, 255);
glBindTexture(GL_TEXTURE_2D, _id);
glBegin(GL_QUADS);
glTexCoord2f(0.f, 0.f);
glVertex2i(0, 0);
glTexCoord2f(_u, 0.f);
glVertex2i(w, 0);
glTexCoord2f(_u, _v);
glVertex2i(w, h);
glTexCoord2f(0.f, _v);
glVertex2i(0, h);
glEnd();
glDisable(GL_TEXTURE_2D);
}
}
void Texture::clear() {
if (_id != kNoTextureId) {
glDeleteTextures(1, &_id);
_id = kNoTextureId;
}
free(_rgbData);
_rgbData = 0;
_raw16Data = 0;
}
void Texture::readRaw16(const uint8 *src, const Color *pal, int w, int h) {
_raw16Data = src;
uploadDataCLUT(_raw16Data, w, w, h, pal);
}
void Texture::readFont(const uint8 *src) {
_fmt = FMT_RGBA;
const int W = 96 * 8 * 2;
const int H = 8;
uint8 *out = (uint8 *)calloc(1, W * H);
if (out) {
for (int i = 0; i < 96; ++i) {
for (int y = 0; y < 8; ++y) {
uint8 mask = *src++;
for (int x = 0; x < 8; ++x) {
out[y * W + i * 16 + x] = (mask >> (7 - x)) & 1;
}
}
}
Color pal[2];
pal[0].r = pal[0].g = pal[0].b = 0;
pal[1].r = pal[1].g = pal[1].b = 255;
uploadDataCLUT(out, W, W, H, pal);
free(out);
}
}
static uint16 rgb555_to_565(const uint16 color) {
const int r = (color >> 10) & 31;
const int g = (color >> 5) & 31;
const int b = color & 31;
return (r << 11) | (g << 6) | b;
}
void Texture::readRGB555(const uint16 *src, int w, int h) {
_rgbData = (uint8 *)malloc(w * h * sizeof(uint16));
if (!_rgbData) {
return;
}
for (int i = 0; i < w * h; ++i) {
((uint16 *)_rgbData)[i] = rgb555_to_565(src[i]);
}
uploadDataRGB(_rgbData, w * sizeof(uint16), w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
}
struct DrawListEntry {
static const int NUM_VERTICES = 1024;
int color;
int numVertices;
Point vertices[NUM_VERTICES];
};
struct DrawList {
typedef std::vector<DrawListEntry> Entries;
int fillColor;
Entries entries;
int yOffset;
DrawList()
: fillColor(0), yOffset(0) {
}
void clear(uint8 color) {
fillColor = color;
entries.clear();
}
void append(uint8 color, int count, const Point *vertices) {
if (count <= DrawListEntry::NUM_VERTICES) {
DrawListEntry e;
e.color = color;
e.numVertices = count;
memcpy(e.vertices, vertices, count * sizeof(Point));
entries.push_back(e);
}
}
};
static const int SCREEN_W = 320;
static const int SCREEN_H = 200;
static const int DEFAULT_FB_W = 1280;
static const int DEFAULT_FB_H = 800;
static const int NUM_LISTS = 4;
struct GraphicsGL : Graphics {
int _w, _h;
int _fbW, _fbH;
Color _pal[16];
Color *_alphaColor;
Texture _backgroundTex;
Texture _fontTex;
Texture _spritesTex;
int _spritesSizeX, _spritesSizeY;
GLuint _fbPage0;
GLuint _pageTex[NUM_LISTS];
DrawList _drawLists[NUM_LISTS];
struct {
int num;
Point pos;
} _sprite;
GraphicsGL();
virtual ~GraphicsGL() {
}
virtual void init(int targetW, int targetH);
virtual void setFont(const uint8 *src, int w, int h);
virtual void setPalette(const Color *colors, int count);
virtual void setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize);
virtual void drawSprite(int listNum, int num, const Point *pt, uint8 color);
virtual void drawBitmap(int listNum, const uint8 *data, int w, int h, int fmt);
virtual void drawPoint(int listNum, uint8 color, const Point *pt);
virtual void drawQuadStrip(int listNum, uint8 color, const QuadStrip *qs);
virtual void drawStringChar(int listNum, uint8 color, char c, const Point *pt);
virtual void clearBuffer(int listNum, uint8 color);
virtual void copyBuffer(int dstListNum, int srcListNum, int vscroll = 0);
virtual void drawBuffer(int listNum, SystemStub *stub);
virtual void drawRect(int num, uint8 color, const Point *pt, int w, int h);
virtual void drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub);
void initFbo();
void drawVerticesFlat(int count, const Point *vertices);
void drawVerticesTex(int count, const Point *vertices);
void drawVerticesToFb(uint8 color, int count, const Point *vertices);
};
GraphicsGL::GraphicsGL() {
_fixUpPalette = FIXUP_PALETTE_NONE;
memset(_pal, 0, sizeof(_pal));
_alphaColor = &_pal[ALPHA_COLOR_INDEX];
_spritesSizeX = _spritesSizeY = 0;
_sprite.num = -1;
}
void GraphicsGL::init(int targetW, int targetH) {
Graphics::init(targetW, targetH);
_fbW = (targetW <= 0) ? DEFAULT_FB_W : targetW;
_fbH = (targetH <= 0) ? DEFAULT_FB_H : targetH;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
const char *exts = (const char *)glGetString(GL_EXTENSIONS);
const bool npotTex = hasExtension(exts, "GL_ARB_texture_non_power_of_two");
const bool hasFbo = hasExtension(exts, "GL_ARB_framebuffer_object");
_backgroundTex.init();
_backgroundTex._npotTex = npotTex;
_fontTex.init();
_fontTex._npotTex = npotTex;
_spritesTex.init();
_spritesTex._npotTex = npotTex;
if (hasFbo) {
setupFboFuncs();
initFbo();
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, 0);
} else {
error("GL_ARB_framebuffer_object is not supported");
}
}
void GraphicsGL::initFbo() {
GLint buffersCount;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &buffersCount);
if (buffersCount < NUM_LISTS) {
error("GL_MAX_COLOR_ATTACHMENTS is %d", buffersCount);
return;
}
_fptr.glGenFramebuffers(1, &_fbPage0);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glGenTextures(NUM_LISTS, _pageTex);
for (int i = 0; i < NUM_LISTS; ++i) {
glBindTexture(GL_TEXTURE_2D, _pageTex[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _fbW, _fbH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
_fptr.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, _pageTex[i], 0);
int status = _fptr.glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
error("glCheckFramebufferStatus failed, ret %d", status);
return;
}
}
glViewport(0, 0, _fbW, _fbH);
const float r = (float)_fbW / SCREEN_W;
glLineWidth(r);
glPointSize(r);
}
void GraphicsGL::setFont(const uint8 *src, int w, int h) {
if (src == 0) {
_fontTex.readFont(_font);
} else {
_fontTex.uploadDataRGB(src, w * 4, w, h, GL_RGBA, GL_UNSIGNED_BYTE);
}
}
void GraphicsGL::setPalette(const Color *colors, int n) {
assert(n <= 16);
for (int i = 0; i < n; ++i) {
_pal[i] = colors[i];
}
if (_fixUpPalette == FIXUP_PALETTE_REDRAW) {
for (int i = 0; i < NUM_LISTS; ++i) {
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + i);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
const int color = _drawLists[i].fillColor;
if (color != COL_BMP) {
assert(color < 16);
glClearColor(_pal[color].r / 255.f, _pal[color].g / 255.f, _pal[color].b / 255.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
glScalef((float)_fbW / SCREEN_W, (float)_fbH / SCREEN_H, 1);
DrawList::Entries::const_iterator it = _drawLists[i].entries.begin();
for (; it != _drawLists[i].entries.end(); ++it) {
DrawListEntry e = *it;
if (e.color < 16) {
glColor4ub(_pal[e.color].r, _pal[e.color].g, _pal[e.color].b, 255);
drawVerticesFlat(e.numVertices, e.vertices);
} else if (e.color == COL_ALPHA) {
glColor4ub(_alphaColor->r, _alphaColor->g, _alphaColor->b, 192);
drawVerticesFlat(e.numVertices, e.vertices);
}
}
glLoadIdentity();
glScalef(1., 1., 1.);
}
}
}
void GraphicsGL::setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) {
_spritesTex.uploadDataRGB(src, w * 4, w, h, GL_RGBA, GL_UNSIGNED_BYTE);
_spritesSizeX = xSize;
_spritesSizeY = ySize;
}
static void drawTexQuad(const int *pos, const float *uv, GLuint tex) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
glTexCoord2f(uv[0], uv[1]);
glVertex2i(pos[0], pos[1]);
glTexCoord2f(uv[2], uv[1]);
glVertex2i(pos[2], pos[1]);
glTexCoord2f(uv[2], uv[3]);
glVertex2i(pos[2], pos[3]);
glTexCoord2f(uv[0], uv[3]);
glVertex2i(pos[0], pos[3]);
glEnd();
glDisable(GL_TEXTURE_2D);
}
static void drawSpriteHelper(const Point *pt, int num, int xSize, int ySize, int texId) {
const int wSize = 18;
const int hSize = 18;
const int pos[4] = {
pt->x, pt->y,
pt->x + wSize, pt->y + hSize
};
int u = num % xSize;
int v = num / ySize;
const float uv[4] = {
u * 1.f / xSize, v * 1.f / ySize,
(u + 1) * 1.f / xSize, (v + 1) * 1.f / ySize
};
glColor4ub(255, 255, 255, 255);
drawTexQuad(pos, uv, texId);
}
void GraphicsGL::drawSprite(int listNum, int num, const Point *pt, uint8 color) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
glScalef((float)_fbW / SCREEN_W, (float)_fbH / SCREEN_H, 1);
drawSpriteHelper(pt, num, _spritesSizeX, _spritesSizeY, _spritesTex._id);
glLoadIdentity();
glScalef(1., 1., 1.);
}
void GraphicsGL::drawBitmap(int listNum, const uint8 *data, int w, int h, int fmt) {
_backgroundTex._fmt = fmt;
switch (fmt) {
case FMT_CLUT:
_backgroundTex.readRaw16(data, _pal, w, h);
break;
case FMT_RGB:
_backgroundTex.clear();
_backgroundTex.uploadDataRGB(data, w * 3, w, h, GL_RGB, GL_UNSIGNED_BYTE);
break;
case FMT_RGB555:
_backgroundTex.clear();
_backgroundTex.readRGB555((const uint16 *)data, w, h);
break;
default:
break;
}
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
_backgroundTex.draw(_fbW, _fbH);
_drawLists[listNum].clear(COL_BMP);
}
void GraphicsGL::drawVerticesToFb(uint8 color, int count, const Point *vertices) {
glScalef((float)_fbW / SCREEN_W, (float)_fbH / SCREEN_H, 1);
if (color == COL_PAGE) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _pageTex[0]);
glColor4f(1., 1., 1., 1.);
drawVerticesTex(count, vertices);
glDisable(GL_TEXTURE_2D);
} else {
if (color == COL_ALPHA) {
glColor4ub(_alphaColor->r, _alphaColor->g, _alphaColor->b, 192);
} else {
assert(color < 16);
glColor4ub(_pal[color].r, _pal[color].g, _pal[color].b, 255);
}
drawVerticesFlat(count, vertices);
}
glLoadIdentity();
glScalef(1., 1., 1.);
}
void GraphicsGL::drawPoint(int listNum, uint8 color, const Point *pt) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
drawVerticesToFb(color, 1, pt);
if (_fixUpPalette != FIXUP_PALETTE_NONE) {
_drawLists[listNum].append(color, 1, pt);
}
}
void GraphicsGL::drawQuadStrip(int listNum, uint8 color, const QuadStrip *qs) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
drawVerticesToFb(color, qs->numVertices, qs->vertices);
if (_fixUpPalette != FIXUP_PALETTE_NONE) {
_drawLists[listNum].append(color, qs->numVertices, qs->vertices);
}
}
void GraphicsGL::drawStringChar(int listNum, uint8 color, char c, const Point *pt) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
glScalef((float)_fbW / SCREEN_W, (float)_fbH / SCREEN_H, 1);
glColor4ub(_pal[color].r, _pal[color].g, _pal[color].b, 255);
if (_fontTex._h == 8) {
const int pos[4] = {
pt->x, pt->y,
pt->x + 8, pt->y + 8
};
const float uv[4] = {
(c - 0x20) * 16.f / _fontTex._w, 0.f,
(c - 0x20) * 16.f / _fontTex._w + 1 * 8.f / _fontTex._w, 1.f
};
drawTexQuad(pos, uv, _fontTex._id);
} else {
const int pos[4] = {
pt->x - 8, pt->y,
pt->x, pt->y + 8
};
float uv[4];
uv[0] = (c % 16) * 16 / 256.f;
uv[2] = uv[0] + 16 / 256.f;
uv[1] = (c / 16) * 16 / 256.f;
uv[3] = uv[1] + 16 / 256.f;
drawTexQuad(pos, uv, _fontTex._id);
}
glLoadIdentity();
glScalef(1., 1., 1.);
}
void GraphicsGL::drawVerticesFlat(int count, const Point *vertices) {
switch (count) {
case 1:
glBegin(GL_POINTS);
glVertex2i(vertices[0].x, vertices[0].y);
glEnd();
break;
case 2:
glBegin(GL_LINES);
if (vertices[1].x > vertices[0].x) {
glVertex2i(vertices[0].x, vertices[0].y);
glVertex2i(vertices[1].x + 1, vertices[1].y);
} else {
glVertex2i(vertices[1].x, vertices[1].y);
glVertex2i(vertices[0].x + 1, vertices[0].y);
}
glEnd();
break;
default:
glBegin(GL_QUAD_STRIP);
for (int i = 0; i < count / 2; ++i) {
const int j = count - 1 - i;
if (vertices[j].x > vertices[i].x) {
glVertex2i(vertices[i].x, vertices[i].y);
glVertex2i(vertices[j].x + 1, vertices[j].y);
} else {
glVertex2i(vertices[j].x, vertices[j].y);
glVertex2i(vertices[i].x + 1, vertices[i].y);
}
}
glEnd();
break;
}
}
void GraphicsGL::drawVerticesTex(int count, const Point *vertices) {
if (count < 4) {
warning("Invalid vertices count for drawing mode 0x11", count);
return;
}
glBegin(GL_QUAD_STRIP);
for (int i = 0; i < count / 2; ++i) {
const int j = count - 1 - i;
if (vertices[j].x > vertices[i].y) {
glTexCoord2f(vertices[i].x / 320., vertices[i].y / 200.);
glVertex2i(vertices[i].x, vertices[i].y);
glTexCoord2f((vertices[j].x + 1) / 320., vertices[j].y / 200.);
glVertex2i((vertices[j].x + 1), vertices[j].y);
} else {
glTexCoord2f(vertices[j].x / 320., vertices[j].y / 200.);
glVertex2i(vertices[j].x, vertices[j].y);
glTexCoord2f((vertices[i].x + 1) / 320., vertices[i].y / 200.);
glVertex2i((vertices[i].x + 1), vertices[i].y);
}
}
glEnd();
}
void GraphicsGL::clearBuffer(int listNum, uint8 color) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + listNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
assert(color < 16);
glClearColor(_pal[color].r / 255.f, _pal[color].g / 255.f, _pal[color].b / 255.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
_drawLists[listNum].clear(color);
}
static void drawTextureFb(GLuint tex, int w, int h, int vscroll) {
glColor4ub(255, 255, 255, 255);
const int pos[] = {
0, vscroll,
w, h + vscroll
};
const float uv[] = {
0., 0.,
1., 1.
};
drawTexQuad(pos, uv, tex);
}
void GraphicsGL::copyBuffer(int dstListNum, int srcListNum, int vscroll) {
assert(dstListNum < NUM_LISTS && srcListNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, _fbPage0);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + dstListNum);
glViewport(0, 0, _fbW, _fbH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _fbW, 0, _fbH, 0, 1);
const int yoffset = vscroll * _fbH / (SCREEN_H - 1);
drawTextureFb(_pageTex[srcListNum], _fbW, _fbH, yoffset);
_drawLists[dstListNum] = _drawLists[srcListNum];
_drawLists[dstListNum].yOffset = vscroll;
}
static void dumpPalette(const Color *pal) {
static const int SZ = 32;
int x2, x1 = 0;
for (int i = 0; i < 16; ++i) {
x2 = x1 + SZ;
glColor4ub(pal[i].r, pal[i].g, pal[i].b, 255);
glBegin(GL_QUADS);
glVertex2i(x1, 0);
glVertex2i(x2, 0);
glVertex2i(x2, SZ);
glVertex2i(x1, SZ);
glEnd();
x1 = x2;
}
}
void GraphicsGL::drawBuffer(int listNum, SystemStub *stub) {
assert(listNum < NUM_LISTS);
_fptr.glBindFramebuffer(GL_FRAMEBUFFER, 0);
float ar[4];
stub->prepareScreen(_w, _h, ar);
glViewport(0, 0, _w, _h);
glClearColor(0., 0., 0., 1.);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _w, _h, 0, 0, 1);
glPushMatrix();
glTranslatef(ar[0] * _w, ar[1] * _h, 0.);
glScalef(ar[2], ar[3], 1.);
drawTextureFb(_pageTex[listNum], _w, _h, 0);
if (0) {
glDisable(GL_TEXTURE_2D);
dumpPalette(_pal);
}
glPopMatrix();
stub->updateScreen();
}
void GraphicsGL::drawRect(int num, uint8 color, const Point *pt, int w, int h) {
// ignore 'num' target framebuffer as this is only used for the title screen with the 3DO version
assert(color < 16);
glColor4ub(_pal[color].r, _pal[color].g, _pal[color].b, 255);
glScalef((float)_fbW / SCREEN_W, (float)_fbH / SCREEN_H, 1);
const int x1 = pt->x;
const int y1 = pt->y;
const int x2 = x1 + w - 1;
const int y2 = y1 + h - 1;
glBegin(GL_LINES);
// horizontal
glVertex2i(x1, y1);
glVertex2i(x2, y1);
glVertex2i(x1, y2);
glVertex2i(x2, y2);
// vertical
glVertex2i(x1, y1);
glVertex2i(x1, y2);
glVertex2i(x2, y1);
glVertex2i(x2, y2);
glEnd();
}
void GraphicsGL::drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) {
if (fmt == FMT_RGB555) {
}
}
Graphics *GraphicsGL_create() {
return new GraphicsGL();
}
#endif
} // namespace Awe

View File

@@ -0,0 +1,507 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/managed_surface.h"
#include "common/textconsole.h"
#include "awe/gfx.h"
#include "awe/system_stub.h"
namespace Awe {
struct GraphicsSoft : public Gfx {
typedef void (GraphicsSoft:: *drawLine)(int16 x1, int16 x2, int16 y, uint8 col);
uint8 *_pagePtrs[4] = { nullptr };
uint8 *_drawPagePtr = { nullptr };
int _u = 0, _v = 0;
int _w = 0, _h = 0;
int _byteDepth = 0;
Color _pal[16];
bool _palChanged = false;
GraphicsSoft();
~GraphicsSoft();
int xScale(int x) const {
return (x * _u) >> 16;
}
int yScale(int y) const {
return (y * _v) >> 16;
}
void setSize(int w, int h);
void drawPolygon(uint8 color, const QuadStrip &qs);
void drawChar(uint8 c, uint16 x, uint16 y, uint8 color);
void drawSpriteMask(int x, int y, uint8 color, const uint8 *data);
void drawPoint(int16 x, int16 y, uint8 color);
void drawLineT(int16 x1, int16 x2, int16 y, uint8 color);
void drawLineN(int16 x1, int16 x2, int16 y, uint8 color);
void drawLineP(int16 x1, int16 x2, int16 y, uint8 color);
uint8 *getPagePtr(uint8 page);
int getPageSize() const {
return _w * _h * _byteDepth;
}
void setWorkPagePtr(uint8 page);
void init(int targetW, int targetH) override;
void setFont(const uint8 *src, int w, int h) override;
void setPalette(const Color *colors, int count) override;
void setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) override;
void drawSprite(int buffer, int num, const Point *pt, uint8 color) override;
void drawBitmap(int buffer, const uint8 *data, int w, int h, int fmt) override;
void drawPoint(int buffer, uint8 color, const Point *pt) override;
void drawQuadStrip(int buffer, uint8 color, const QuadStrip *qs) override;
void drawStringChar(int buffer, uint8 color, char c, const Point *pt) override;
void clearBuffer(int num, uint8 color) override;
void copyBuffer(int dst, int src, int vscroll = 0) override;
void drawBuffer(int num, SystemStub *stub) override;
void drawRect(int num, uint8 color, const Point *pt, int w, int h) override;
void drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) override;
};
GraphicsSoft::GraphicsSoft() {
_fixUpPalette = FIXUP_PALETTE_NONE;
memset(_pagePtrs, 0, sizeof(_pagePtrs));
}
GraphicsSoft::~GraphicsSoft() {
for (int i = 0; i < 4; ++i) {
free(_pagePtrs[i]);
_pagePtrs[i] = nullptr;
}
}
void GraphicsSoft::setSize(int w, int h) {
_u = (w << 16) / GFX_W;
_v = (h << 16) / GFX_H;
_w = w;
_h = h;
_byteDepth = _format.bytesPerPixel;
assert(_byteDepth == 1 || _byteDepth == 2);
for (int i = 0; i < 4; ++i) {
uint8 *tmp = (uint8 *)realloc(_pagePtrs[i], getPageSize());
if (!tmp) {
error("Not enough memory to allocate offscreen buffers");
}
_pagePtrs[i] = tmp;
memset(_pagePtrs[i], 0, getPageSize());
}
setWorkPagePtr(2);
}
static uint32 calcStep(const Point &p1, const Point &p2, uint16 &dy) {
dy = p2.y - p1.y;
const uint16 delta = (dy <= 1) ? 1 : dy;
return ((p2.x - p1.x) * (0x4000 / delta)) << 2;
}
void GraphicsSoft::drawPolygon(uint8 color, const QuadStrip &quadStrip) {
QuadStrip qs = quadStrip;
if (_w != GFX_W || _h != GFX_H) {
for (int i = 0; i < qs.numVertices; ++i) {
qs.vertices[i].scale(_u, _v);
}
}
int i = 0;
int j = qs.numVertices - 1;
int16 x2 = qs.vertices[i].x;
int16 x1 = qs.vertices[j].x;
int16 hliney = MIN(qs.vertices[i].y, qs.vertices[j].y);
++i;
--j;
drawLine pdl;
switch (color) {
default:
pdl = &GraphicsSoft::drawLineN;
break;
case COL_PAGE:
pdl = &GraphicsSoft::drawLineP;
break;
case COL_ALPHA:
pdl = &GraphicsSoft::drawLineT;
break;
}
uint32 cpt1 = x1 << 16;
uint32 cpt2 = x2 << 16;
int numVertices = qs.numVertices;
while (true) {
numVertices -= 2;
if (numVertices == 0) {
return;
}
uint16 h;
const uint32 step1 = calcStep(qs.vertices[j + 1], qs.vertices[j], h);
const uint32 step2 = calcStep(qs.vertices[i - 1], qs.vertices[i], h);
++i;
--j;
cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF;
cpt2 = (cpt2 & 0xFFFF0000) | 0x8000;
if (h == 0) {
cpt1 += step1;
cpt2 += step2;
} else {
while (h--) {
if (hliney >= 0) {
x1 = cpt1 >> 16;
x2 = cpt2 >> 16;
if (x1 < _w && x2 >= 0) {
if (x1 < 0) x1 = 0;
if (x2 >= _w) x2 = _w - 1;
(this->*pdl)(x1, x2, hliney, color);
}
}
cpt1 += step1;
cpt2 += step2;
++hliney;
if (hliney >= _h) return;
}
}
}
}
void GraphicsSoft::drawChar(uint8 c, uint16 x, uint16 y, uint8 color) {
if (x <= GFX_W - 8 && y <= GFX_H - 8) {
x = xScale(x);
y = yScale(y);
const uint8 *ft = FONT + (c - 0x20) * 8;
const int offset = (x + y * _w) * _byteDepth;
if (_byteDepth == 1) {
for (int j = 0; j < 8; ++j) {
const uint8 ch = ft[j];
for (int i = 0; i < 8; ++i) {
if (ch & (1 << (7 - i))) {
_drawPagePtr[offset + j * _w + i] = color;
}
}
}
} else if (_byteDepth == 2) {
const uint16 rgbColor = _format.RGBToColor(
_pal[color].r, _pal[color].g, _pal[color].b);
for (int j = 0; j < 8; ++j) {
const uint8 ch = ft[j];
for (int i = 0; i < 8; ++i) {
if (ch & (1 << (7 - i))) {
((uint16 *)(_drawPagePtr + offset))[j * _w + i] = rgbColor;
}
}
}
}
}
}
void GraphicsSoft::drawSpriteMask(int x, int y, uint8 color, const uint8 *data) {
const int w = *data++;
x = xScale(x - w / 2);
const int h = *data++;
y = yScale(y - h / 2);
assert(_byteDepth == 1);
for (int j = 0; j < h; ++j) {
const int yoffset = y + j;
for (int i = 0; i <= w / 16; ++i) {
const uint16 mask = READ_BE_UINT16(data); data += 2;
if (yoffset < 0 || yoffset >= _h) {
continue;
}
const int xoffset = x + i * 16;
for (int b = 0; b < 16; ++b) {
if (xoffset + b < 0 || xoffset + b >= _w) {
continue;
}
if (mask & (1 << (15 - b))) {
_drawPagePtr[yoffset * _w + xoffset + b] = color;
}
}
}
}
}
static void blend_rgb555(uint16 *dst, const uint16 b) {
static const uint16 RB_MASK = 0x7c1f;
static const uint16 G_MASK = 0x03e0;
const uint16 a = *dst;
if ((a & 0x8000) == 0) { // use bit 15 to prevent additive blending
uint16 r = 0x8000;
r |= (((a & RB_MASK) + (b & RB_MASK)) >> 1) & RB_MASK;
r |= (((a & G_MASK) + (b & G_MASK)) >> 1) & G_MASK;
*dst = r;
}
}
void GraphicsSoft::drawPoint(int16 x, int16 y, uint8 color) {
x = xScale(x);
y = yScale(y);
const int offset = (y * _w + x) * _byteDepth;
if (_byteDepth == 1) {
switch (color) {
case COL_ALPHA:
_drawPagePtr[offset] |= 8;
break;
case COL_PAGE:
_drawPagePtr[offset] = *(_pagePtrs[0] + offset);
break;
default:
_drawPagePtr[offset] = color;
break;
}
} else if (_byteDepth == 2) {
switch (color) {
case COL_ALPHA: {
const Color &c = _pal[ALPHA_COLOR_INDEX];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
blend_rgb555((uint16 *)(_drawPagePtr + offset), rgbColor);
break;
}
case COL_PAGE:
*(uint16 *)(_drawPagePtr + offset) = *(uint16 *)(_pagePtrs[0] + offset);
break;
default: {
const Color &c = _pal[color];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
*(uint16 *)(_drawPagePtr + offset) = rgbColor;
break;
}
}
}
}
void GraphicsSoft::drawLineT(int16 x1, int16 x2, int16 y, uint8 color) {
const int16 xmax = MAX(x1, x2);
const int16 xmin = MIN(x1, x2);
const int w = xmax - xmin + 1;
const int offset = (y * _w + xmin) * _byteDepth;
if (_byteDepth == 1) {
for (int i = 0; i < w; ++i) {
_drawPagePtr[offset + i] |= 8;
}
} else if (_byteDepth == 2) {
const Color &c = _pal[ALPHA_COLOR_INDEX];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
uint16 *p = (uint16 *)(_drawPagePtr + offset);
for (int i = 0; i < w; ++i) {
blend_rgb555(p + i, rgbColor);
}
}
}
void GraphicsSoft::drawLineN(int16 x1, int16 x2, int16 y, uint8 color) {
const int16 xmax = MAX(x1, x2);
const int16 xmin = MIN(x1, x2);
const int w = xmax - xmin + 1;
const int offset = (y * _w + xmin) * _byteDepth;
if (_byteDepth == 1) {
memset(_drawPagePtr + offset, color, w);
} else if (_byteDepth == 2) {
const Color &c = _pal[color];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
uint16 *p = (uint16 *)(_drawPagePtr + offset);
for (int i = 0; i < w; ++i) {
p[i] = rgbColor;
}
}
}
void GraphicsSoft::drawLineP(int16 x1, int16 x2, int16 y, uint8 color) {
if (_drawPagePtr == _pagePtrs[0]) {
return;
}
const int16 xmax = MAX(x1, x2);
const int16 xmin = MIN(x1, x2);
const int w = xmax - xmin + 1;
const int offset = (y * _w + xmin) * _byteDepth;
memcpy(_drawPagePtr + offset, _pagePtrs[0] + offset, w * _byteDepth);
}
uint8 *GraphicsSoft::getPagePtr(uint8 page) {
assert(page < 4);
return _pagePtrs[page];
}
void GraphicsSoft::setWorkPagePtr(uint8 page) {
_drawPagePtr = getPagePtr(page);
}
void GraphicsSoft::init(int targetW, int targetH) {
Gfx::init(targetW, targetH);
setSize(targetW, targetH);
}
void GraphicsSoft::setFont(const uint8 *src, int w, int h) {
if (_is1991) {
// no-op for 1991
}
}
void GraphicsSoft::setPalette(const Color *colors, int count) {
count = MIN(count, 16);
for (int i = 0; i < count; i++) {
_pal[i].r = colors[i].r;
_pal[i].g = colors[i].g;
_pal[i].b = colors[i].b;
}
_palChanged = true;
}
void GraphicsSoft::setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) {
if (_is1991) {
// no-op for 1991
}
}
void GraphicsSoft::drawSprite(int buffer, int num, const Point *pt, uint8 color) {
if (_is1991) {
if (num < SHAPES_MASK_COUNT) {
setWorkPagePtr(buffer);
const uint8 *data = SHAPES_MASK_DATA + SHAPES_MASK_OFFSET[num];
drawSpriteMask(pt->x, pt->y, color, data);
}
}
}
void GraphicsSoft::drawBitmap(int buffer, const uint8 *data, int w, int h, int fmt) {
switch (_byteDepth) {
case 1:
if (fmt == FMT_CLUT && _w == w && _h == h) {
memcpy(getPagePtr(buffer), data, w * h);
return;
}
break;
case 2:
if (fmt == FMT_RGB555 && _w == w && _h == h) {
memcpy(getPagePtr(buffer), data, getPageSize());
return;
}
break;
default:
break;
}
warning("GraphicsSoft::drawBitmap() unhandled fmt %d w %d h %d", fmt, w, h);
}
void GraphicsSoft::drawPoint(int buffer, uint8 color, const Point *pt) {
setWorkPagePtr(buffer);
drawPoint(pt->x, pt->y, color);
}
void GraphicsSoft::drawQuadStrip(int buffer, uint8 color, const QuadStrip *qs) {
setWorkPagePtr(buffer);
drawPolygon(color, *qs);
}
void GraphicsSoft::drawStringChar(int buffer, uint8 color, char c, const Point *pt) {
setWorkPagePtr(buffer);
drawChar(c, pt->x, pt->y, color);
}
void GraphicsSoft::clearBuffer(int num, uint8 color) {
if (_byteDepth == 1) {
memset(getPagePtr(num), color, getPageSize());
} else if (_byteDepth == 2) {
const Color &c = _pal[color];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
uint16 *p = (uint16 *)getPagePtr(num);
for (int i = 0; i < _w * _h; ++i) {
p[i] = rgbColor;
}
}
}
void GraphicsSoft::copyBuffer(int dst, int src, int vscroll) {
if (vscroll == 0) {
memcpy(getPagePtr(dst), getPagePtr(src), getPageSize());
} else if (vscroll >= -199 && vscroll <= 199) {
const int dy = yScale(vscroll);
if (dy < 0) {
memcpy(getPagePtr(dst), getPagePtr(src) - dy * _w * _byteDepth, (_h + dy) * _w * _byteDepth);
} else {
memcpy(getPagePtr(dst) + dy * _w * _byteDepth, getPagePtr(src), (_h - dy) * _w * _byteDepth);
}
}
}
void GraphicsSoft::drawBuffer(int num, SystemStub *stub) {
int w, h;
float ar[4];
stub->prepareScreen(w, h, ar);
if (_palChanged) {
stub->setPalette(_pal);
_palChanged = false;
}
Graphics::Surface s;
s.setPixels(getPagePtr(num));
s.w = s.pitch = w;
s.h = h;
s.format = (_byteDepth == 1) ?
Graphics::PixelFormat::createFormatCLUT8() :
_format;
stub->setScreenPixels(s);
stub->updateScreen();
}
void GraphicsSoft::drawRect(int num, uint8 color, const Point *pt, int w, int h) {
assert(_byteDepth == 2);
setWorkPagePtr(num);
const Color &c = _pal[color];
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
const int x1 = xScale(pt->x);
const int y1 = yScale(pt->y);
const int x2 = xScale(pt->x + w - 1);
const int y2 = yScale(pt->y + h - 1);
// horizontal
for (int x = x1; x <= x2; ++x) {
*(uint16 *)(_drawPagePtr + (y1 * _w + x) * _byteDepth) = rgbColor;
*(uint16 *)(_drawPagePtr + (y2 * _w + x) * _byteDepth) = rgbColor;
}
// vertical
for (int y = y1; y <= y2; ++y) {
*(uint16 *)(_drawPagePtr + (y * _w + x1) * _byteDepth) = rgbColor;
*(uint16 *)(_drawPagePtr + (y * _w + x2) * _byteDepth) = rgbColor;
}
}
void GraphicsSoft::drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) {
if (fmt == FMT_RGB555) {
stub->setScreenPixels(src);
stub->updateScreen();
}
}
Gfx *GraphicsSoft_create() {
return new GraphicsSoft();
}
} // namespace Awe

124
engines/awe/intern.h Normal file
View File

@@ -0,0 +1,124 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_INTERN_H
#define AWE_INTERN_H
#include "common/algorithm.h"
#include "common/endian.h"
#include "common/language.h"
#include "common/rect.h"
namespace Awe {
using Common::Language;
enum {
kPartCopyProtection = 16000,
kPartIntro = 16001,
kPartWater = 16002,
kPartPrison = 16003,
kPartCite = 16004,
kPartArene = 16005,
kPartLuxe = 16006,
kPartFinal = 16007,
kPartPassword = 16008
};
enum {
kPaulaFreq = 7159092
};
struct Ptr {
uint8 *pc = nullptr;
bool byteSwap = false;
uint8 fetchByte() {
return *pc++;
}
uint16 fetchWord() {
const uint16 i = byteSwap ? READ_LE_UINT16(pc) : READ_BE_UINT16(pc);
pc += 2;
return i;
}
};
struct Point : public Common::Point {
Point() : Common::Point() {}
Point(int16 xx, int16 yy) : Common::Point(xx, yy) {}
Point(const Point &p) : Common::Point(p) {}
void scale(int u, int v) {
x = (x * u) >> 16;
y = (y * v) >> 16;
}
};
struct QuadStrip {
enum {
MAX_VERTICES = 70
};
uint8 numVertices = 0;
Point vertices[MAX_VERTICES];
};
struct Color {
byte rgb[3];
uint8 &r = rgb[0];
uint8 &g = rgb[1];
uint8 &b = rgb[2];
Color() {
clear();
}
void clear() {
r = g = b = 0;
}
};
struct Frac {
static const int BITS = 16;
static const int MASK = (1 << BITS) - 1;
uint32 inc = 0;
uint64 offset = 0;
void reset(int n, int d) {
inc = (((int64)n) << BITS) / d;
offset = 0;
}
uint32 getInt() const {
return offset >> BITS;
}
uint32 getFrac() const {
return offset & MASK;
}
int interpolate(int sample1, int sample2) const {
const int fp = getFrac();
return (sample1 * (MASK - fp) + sample2 * fp) >> BITS;
}
};
} // namespace Awe
#endif

112
engines/awe/metaengine.cpp Normal file
View File

@@ -0,0 +1,112 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "awe/metaengine.h"
#include "awe/detection.h"
#include "awe/awe.h"
namespace Awe {
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_COPY_PROTECTION,
{
_s("Enable title and copy protection screens (if present)"),
_s("Displays title and copy protection screens that would otherwise be bypassed by default."),
"copy_protection",
false,
0,
0
},
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
struct KeybindingRecord {
KeybindingAction _action;
const char *_id;
const char *_desc;
const char *_key1;
const char *_key2;
const char *_joy;
};
static const KeybindingRecord GAME_KEYS[] = {
{ KEYBIND_UP, "UP", _s("Up"), "UP", nullptr, "JOY_UP"},
{ KEYBIND_DOWN, "DOWN", _s("Down"), "DOWN", nullptr, "JOY_DOWN"},
{ KEYBIND_LEFT, "LEFT", _s("Left"), "LEFT", nullptr, "JOY_LEFT"},
{ KEYBIND_RIGHT, "RIGHT", _s("Right"), "RIGHT", nullptr, "JOY_RIGHT"},
{ KEYBIND_SELECT, "SELECT", _s("Select/Kick/Run"), "SPACE", "RETURN", "JOY_A" },
{ KEYBIND_JUMP, "JUMP", _s("Jump"), "LSHIFT", "RSHIFT", "JOY_B" },
{ KEYBIND_CODE, "CODE", _s("Enter Level Code"), "c", nullptr, "JOY_X" },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
};
} // End of namespace Awe
const char *AweMetaEngine::getName() const {
return "awe";
}
const ADExtraGuiOptionsMap *AweMetaEngine::getAdvancedExtraGuiOptions() const {
return Awe::optionsList;
}
Common::Error AweMetaEngine::createInstance(OSystem *syst, Engine **engine, const Awe::AweGameDescription *desc) const {
*engine = new Awe::AweEngine(syst, (const Awe::AweGameDescription *)desc);
return Common::kNoError;
}
bool AweMetaEngine::hasFeature(MetaEngineFeature f) const {
return false;
}
Common::Array<Common::Keymap *> AweMetaEngine::initKeymaps(const char *target) const {
Common::KeymapArray keymapArray;
Common::Keymap *keyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, "got", _s("Game Keys"));
keymapArray.push_back(keyMap);
for (const Awe::KeybindingRecord *r = Awe::GAME_KEYS; r->_id; ++r) {
Common::Action *act = new Common::Action(r->_id, _(r->_desc));
act->setCustomEngineActionEvent(r->_action);
act->allowKbdRepeats();
act->addDefaultInputMapping(r->_key1);
if (r->_key2)
act->addDefaultInputMapping(r->_key2);
if (r->_joy)
act->addDefaultInputMapping(r->_joy);
keyMap->addAction(act);
}
return keymapArray;
}
#if PLUGIN_ENABLED_DYNAMIC(AWE)
REGISTER_PLUGIN_DYNAMIC(AWE, PLUGIN_TYPE_ENGINE, AweMetaEngine);
#else
REGISTER_PLUGIN_STATIC(AWE, PLUGIN_TYPE_ENGINE, AweMetaEngine);
#endif

61
engines/awe/metaengine.h Normal file
View File

@@ -0,0 +1,61 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_METAENGINE_H
#define AWE_METAENGINE_H
#include "awe/detection.h"
#include "engines/advancedDetector.h"
namespace Awe {
enum KeybindingAction {
KEYBIND_NONE,
KEYBIND_UP,
KEYBIND_DOWN,
KEYBIND_LEFT,
KEYBIND_RIGHT,
KEYBIND_SELECT,
KEYBIND_JUMP,
KEYBIND_CODE
};
} // namespace Awe
class AweMetaEngine : public AdvancedMetaEngine<Awe::AweGameDescription> {
public:
const char *getName() const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Awe::AweGameDescription *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;
Common::Array<Common::Keymap *> initKeymaps(const char *target) const override;
};
#endif

33
engines/awe/module.mk Normal file
View File

@@ -0,0 +1,33 @@
MODULE := engines/awe
MODULE_OBJS = \
metaengine.o \
awe.o \
aifc_player.o \
bitmap.o \
engine.o \
graphics_gl.o \
graphics_soft.o \
pak.o \
resource.o \
resource_3do.o \
resource_nth.o \
resource_win31.o \
script.o \
sfx_player.o \
sound.o \
static_res.o \
system_stub.o \
unpack.o \
video.o
# This module can be built as a plugin
ifeq ($(ENABLE_AWE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

152
engines/awe/pak.cpp Normal file
View File

@@ -0,0 +1,152 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/pak.h"
#include "awe/detection.h"
namespace Awe {
// static const uint32 XOR_KEY1 = 0x31111612;
// static const uint32 CHECKSUM = 0x20202020;
static const uint32 XOR_KEY2 = 0x22683297;
static uint8 *decode_toodc(uint8 *p, int count) {
uint32 key = XOR_KEY2;
uint32 acc = 0;
for (int i = 0; i < count; ++i) {
uint8 *q = p + i * 4;
const uint32 data = READ_LE_UINT32(q) ^ key;
uint32 r = (q[2] + q[1] + q[0]) ^ q[3];
r += acc;
key += r;
acc += 0x4D;
WRITE_LE_UINT32(q, data);
}
return p + 4;
}
const char *Pak::FILENAME = "Pak01.pak";
Pak::Pak()
: _entries(nullptr), _entriesCount(0) {
}
Pak::~Pak() {
close();
}
void Pak::open(const char *dataPath) {
_f.open(Common::Path(FILENAME));
}
void Pak::close() {
free(_entries);
_entries = nullptr;
_entriesCount = 0;
}
static int comparePakEntry(const void *a, const void *b) {
return scumm_stricmp(((const PakEntry *)a)->name, ((const PakEntry *)b)->name);
}
void Pak::readEntries() {
uint8 header[12];
memset(header, 0, sizeof(header));
_f.read(header, sizeof(header));
if (_f.err() || memcmp(header, "PACK", 4) != 0) {
return;
}
const uint32 entriesOffset = READ_LE_UINT32(header + 4);
_f.seek(entriesOffset);
const uint32 entriesSize = READ_LE_UINT32(header + 8);
_entriesCount = entriesSize / 0x40;
debugC(kDebugPak, "Pak::readEntries() entries count %d", _entriesCount);
_entries = (PakEntry *)calloc(_entriesCount, sizeof(PakEntry));
if (!_entries) {
_entriesCount = 0;
return;
}
for (int i = 0; i < _entriesCount; ++i) {
uint8 buf[0x40];
_f.read(buf, sizeof(buf));
if (_f.err()) {
break;
}
const char *name = (const char *)buf;
if (strncmp(name, "dlx/", 4) != 0) {
continue;
}
PakEntry *e = &_entries[i];
Common::strcpy_s(e->name, name + 4);
e->offset = READ_LE_UINT32(buf + 0x38);
e->size = READ_LE_UINT32(buf + 0x3C);
debugC(kDebugPak, "Pak::readEntries() buf '%s' size %d", e->name, e->size);
}
qsort(_entries, _entriesCount, sizeof(PakEntry), comparePakEntry);
#if 0
// the original executable descrambles the (ke)y.txt file and check the last 4 bytes.
// this has been disabled in later re-releases and a key is bundled in the data files
uint8 buf[128];
const PakEntry *e = find("check.txt");
if (e && e->size <= sizeof(buf)) {
uint32 size = 0;
loadData(e, buf, &size);
assert(size >= 4);
const uint32 num = READ_LE_UINT32(buf + size - 4);
assert(num == CHECKSUM);
}
#endif
}
const PakEntry *Pak::find(const char *name) {
debugC(kDebugPak, "Pak::find() '%s'", name);
PakEntry tmp;
Common::strcpy_s(tmp.name, name);
return (const PakEntry *)bsearch(&tmp, _entries, _entriesCount, sizeof(PakEntry), comparePakEntry);
}
void Pak::loadData(const PakEntry *e, uint8 *buf, uint32 *size) {
debugC(kDebugPak, "Pak::loadData() %d bytes from 0x%x", e->size, e->offset);
_f.seek(e->offset);
if (_f.err()) {
*size = 0;
return;
}
_f.read(buf, e->size);
if (e->size > 5 && memcmp(buf, "TooDC", 5) == 0) {
const int dataSize = e->size - 6;
debugC(kDebugPak, "Pak::loadData() encoded TooDC data, size %d", dataSize);
if ((dataSize & 3) != 0) {
// descrambler operates on uint32
warning("Unexpected size %d for encoded TooDC data '%s'", dataSize, e->name);
}
*size = dataSize - 4;
decode_toodc(buf + 6, (dataSize + 3) / 4);
memmove(buf, buf + 10, dataSize - 4);
} else {
*size = e->size;
}
}
} // namespace Awe

57
engines/awe/pak.h Normal file
View File

@@ -0,0 +1,57 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_PAK_H
#define AWE_PAK_H
#include "common/file.h"
#include "awe/intern.h"
namespace Awe {
struct PakEntry {
char name[32] = { '\0' };
uint32 offset = 0;
uint32 size = 0;
};
struct Pak {
static const char *FILENAME;
Common::File _f;
PakEntry *_entries = nullptr;
int _entriesCount = 0;
Pak();
~Pak();
void open(const char *dataPath);
void close();
void readEntries();
const PakEntry *find(const char *name);
void loadData(const PakEntry *e, uint8 *buf, uint32 *size);
};
} // namespace Awe
#endif

695
engines/awe/resource.cpp Normal file
View File

@@ -0,0 +1,695 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "awe/resource.h"
#include "awe/pak.h"
#include "awe/resource_nth.h"
#include "awe/resource_win31.h"
#include "awe/resource_3do.h"
#include "awe/unpack.h"
#include "awe/video.h"
namespace Awe {
static const char *atariDemo = "aw.tos";
static const char *kGameTitleEU = "Another World";
static const char *kGameTitleUS = "Out Of This World";
static const char *kGameTitle15thEdition = "Another World 15th anniversary edition";
static const char *kGameTitle20thEdition = "Another World 20th anniversary edition";
static const int MEMLIST_BMP[] = {
145, 144, 73, 72, 70, 69, 68, 67, -1
};
Resource::Resource(Video *vid, DataType dataType) :
_vid(vid), _dataType(dataType) {
if (_dataType == DT_AMIGA || _dataType == DT_ATARI) {
_amigaMemList = detectAmigaAtari();
} else
_amigaMemList = nullptr;
}
Resource::~Resource() {
free(_demo3Joy.bufPtr);
delete _nth;
delete _win31;
delete _3do;
}
bool Resource::readBank(const MemEntry *me, uint8 *dstBuf) {
bool ret = false;
char name[10];
snprintf(name, sizeof(name), "%s%02x", _bankPrefix, me->bankNum);
Common::File f;
if (f.open(name) || (_dataType == DT_ATARI_DEMO && f.open(atariDemo))) {
f.seek(me->bankPos);
const size_t count = f.read(dstBuf, me->packedSize);
ret = (count == me->packedSize);
if (ret && me->packedSize != me->unpackedSize) {
ret = bytekiller_unpack(dstBuf, me->unpackedSize, dstBuf, me->packedSize);
}
}
return ret;
}
const AmigaMemEntry *Resource::detectAmigaAtari() {
Common::File f;
static const struct {
uint32 bank01Size;
const AmigaMemEntry *entries;
} FILES[] = {
{ 244674, Resource::MEMLIST_AMIGA_FR },
{ 244868, Resource::MEMLIST_AMIGA_EN },
{ 227142, Resource::MEMLIST_ATARI_EN },
{ 0, nullptr }
};
if (f.open("bank01")) {
const uint32 size = f.size();
for (int i = 0; FILES[i].entries; ++i) {
if (FILES[i].bank01Size == size) {
return FILES[i].entries;
}
}
}
return nullptr;
}
const char *Resource::getGameTitle(Language lang) const {
switch (_dataType) {
case DT_15TH_EDITION:
return kGameTitle15thEdition;
case DT_20TH_EDITION:
return kGameTitle20thEdition;
case DT_3DO:
return kGameTitleUS;
case DT_DOS:
if (lang == Common::EN_ANY) {
return kGameTitleUS;
}
/* fall-through */
default:
break;
}
return kGameTitleEU;
}
void Resource::readEntries() {
switch (_dataType) {
case DT_15TH_EDITION:
_numMemList = ENTRIES_COUNT;
_nth = ResourceNth::create(15);
if (_nth && _nth->init()) {
return;
}
break;
case DT_20TH_EDITION:
_numMemList = ENTRIES_COUNT_20TH;
_nth = ResourceNth::create(20);
if (_nth && _nth->init()) {
return;
}
break;
case DT_AMIGA:
case DT_ATARI:
assert(_amigaMemList);
readEntriesAmiga(_amigaMemList, ENTRIES_COUNT);
return;
case DT_DOS:
{
_hasPasswordScreen = false; // DOS demo versions do not have the resources
Common::File f;
if (Common::File::exists("demo01")) {
_bankPrefix = "demo";
}
if (f.open("memlist.bin")) {
MemEntry *me = _memList;
for (;;) {
assert(_numMemList < ARRAYSIZE(_memList));
me->load(&f);
if (me->status == 0xFF) {
const int num = MEMLIST_PARTS[8][1]; // 16008 bytecode
assert(num < _numMemList);
const Common::String bank = Common::String::format(
"%s%02x", _bankPrefix, _memList[num].bankNum);
_hasPasswordScreen = Common::File::exists(bank.c_str());
return;
}
++_numMemList;
++me;
}
}
}
break;
case DT_WIN31:
_numMemList = ENTRIES_COUNT;
_win31 = new ResourceWin31();
if (_win31->readEntries()) {
return;
}
break;
case DT_3DO:
_numMemList = ENTRIES_COUNT;
_3do = new Resource3do();
if (_3do->readEntries()) {
return;
}
break;
case DT_ATARI_DEMO:
{
Common::File f;
if (f.open(atariDemo)) {
static const struct {
uint8 type;
uint8 num;
uint32 offset;
uint16 size;
} DATA[] = {
{ RT_SHAPE, 0x19, 0x50f0, 65146 },
{ RT_PALETTE, 0x17, 0x14f6a, 2048 },
{ RT_BYTECODE, 0x18, 0x1576a, 8368 }
};
_numMemList = ENTRIES_COUNT;
for (int i = 0; i < 3; ++i) {
MemEntry *entry = &_memList[DATA[i].num];
entry->type = DATA[i].type;
entry->bankNum = 15;
entry->bankPos = DATA[i].offset;
entry->packedSize = entry->unpackedSize = DATA[i].size;
}
return;
}
}
break;
}
error("No data files found");
}
void Resource::readEntriesAmiga(const AmigaMemEntry *entries, int count) {
_numMemList = count;
for (int i = 0; i < count; ++i) {
_memList[i].type = entries[i].type;
_memList[i].bankNum = entries[i].bank;
_memList[i].bankPos = entries[i].offset;
_memList[i].packedSize = entries[i].packedSize;
_memList[i].unpackedSize = entries[i].unpackedSize;
}
_memList[count].status = 0xFF;
}
void Resource::dumpEntries() {
if (ConfMan.getBool("dump_scripts") && (_dataType == DT_DOS || _dataType == DT_AMIGA || _dataType == DT_ATARI)) {
for (int i = 0; i < _numMemList; ++i) {
if (_memList[i].unpackedSize == 0) {
continue;
}
if (_memList[i].bankNum == 5 && (_dataType == DT_AMIGA || _dataType == DT_ATARI)) {
continue;
}
uint8 *p = (uint8 *)malloc(_memList[i].unpackedSize);
if (p) {
if (readBank(&_memList[i], p)) {
Common::String fname = Common::String::format("dumps/data_%02x_%d", i, _memList[i].type);
Common::DumpFile out;
if (!out.open(fname.c_str(), true)) {
warning("Resource::dumpEntries(): Can not open dump file %s", fname.c_str());
} else {
out.write(p, _memList[i].unpackedSize);
out.flush();
out.close();
}
}
free(p);
}
}
}
}
void Resource::load() {
while (true) {
MemEntry *me = nullptr;
// get resource with max rankNum
uint8 maxNum = 0;
for (int i = 0; i < _numMemList; ++i) {
MemEntry *it = &_memList[i];
if (it->status == STATUS_TOLOAD && maxNum <= it->rankNum) {
maxNum = it->rankNum;
me = it;
}
}
if (me == nullptr) {
break; // no entry found
}
const int resourceNum = me - _memList;
uint8 *memPtr;
if (me->type == RT_BITMAP) {
memPtr = _vidCurPtr;
} else {
memPtr = _scriptCurPtr;
const uint32 avail = uint32(_vidCurPtr - _scriptCurPtr);
if (me->unpackedSize > avail) {
warning("Resource::load() not enough memory, available=%d", avail);
me->status = STATUS_NULL;
continue;
}
}
if (me->bankNum == 0) {
warning("Resource::load() ec=0x%X (me->bankNum == 0)", 0xF00);
me->status = STATUS_NULL;
} else {
debugC(kDebugBank, "Resource::load() bufPos=0x%x size=%d type=%d pos=0x%x bankNum=%d",
(uint)(memPtr - _memPtrStart), me->packedSize, me->type,
me->bankPos, me->bankNum);
if (readBank(me, memPtr)) {
if (me->type == RT_BITMAP) {
_vid->copyBitmapPtr(_vidCurPtr, me->unpackedSize);
me->status = STATUS_NULL;
} else {
me->bufPtr = memPtr;
me->status = STATUS_LOADED;
_scriptCurPtr += me->unpackedSize;
}
} else {
if (_dataType == DT_DOS && me->bankNum == 12 && me->type == RT_BANK) {
// DOS demo version does not have the bank for this resource
// this should be safe to ignore as the resource does not appear to be used by the game code
me->status = STATUS_NULL;
continue;
}
error("Unable to read resource %d from bank %d", resourceNum, me->bankNum);
}
}
}
}
void Resource::invalidateRes() {
for (int i = 0; i < _numMemList; ++i) {
MemEntry *me = &_memList[i];
if (me->type <= 2 || me->type > 6) {
me->status = STATUS_NULL;
}
}
_scriptCurPtr = _scriptBakPtr;
_vid->_currentPal = 0xFF;
}
void Resource::invalidateAll() {
for (int i = 0; i < _numMemList; ++i) {
_memList[i].status = STATUS_NULL;
}
_scriptCurPtr = _memPtrStart;
_vid->_currentPal = 0xFF;
}
static const uint8 *getSoundsList3DO(int num) {
static const uint8 INTRO7[] = {
0x33, 0xFF
};
static const uint8 WATER7[] = {
0x08, 0x10, 0x2D, 0x30, 0x31, 0x32, 0x35, 0x39, 0x3A, 0x3C,
0x3D, 0x3E, 0x4A, 0x4B, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
0x54, 0xFF
};
static const uint8 PRI1[] = {
0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
0x5E, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x70, 0x71, 0x72, 0x73, 0xFF
};
static const uint8 CITE1[] = {
0x02, 0x03, 0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x63, 0x66, 0x6A, 0x6B, 0x6C,
0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x74, 0x75, 0x77, 0x78, 0x79,
0x7A, 0x7B, 0x7C, 0x88, 0xFF
};
static const uint8 ARENE2[] = {
0x52, 0x57, 0x58, 0x59, 0x5B, 0x84, 0x8B, 0x8C, 0x8E, 0xFF
};
static const uint8 LUXE2[] = {
0x30, 0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C,
0x5D, 0x5E, 0x5F, 0x60, 0x66, 0x67, 0x6B, 0x6C, 0x70, 0x74,
0x75, 0x79, 0x7A, 0x8D, 0xFF
};
static const uint8 FINAL3[] = {
0x08, 0x0E, 0x0F, 0x57, 0xFF
};
switch (num) {
case 2001: return INTRO7;
case 2002: return WATER7;
case 2003: return PRI1;
case 2004: return CITE1;
case 2005: return ARENE2;
case 2006: return LUXE2;
case 2007: return FINAL3;
default: break;
}
return nullptr;
}
void Resource::update(uint16 num, PreloadSoundProc preloadSound, void *data) {
if (num > 16000) {
_nextPart = num;
return;
}
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
if (num >= 3000) {
loadBmp(num);
break;
} else if (num >= 2000) {
break;
}
/* fall-through */
case DT_WIN31:
if (num == 71 || num == 83) {
loadBmp(num);
break;
}
for (int i = 0; MEMLIST_BMP[i] != -1; ++i) {
if (num == MEMLIST_BMP[i]) {
loadBmp(num);
return;
}
}
break;
case DT_3DO:
if (num >= 2000) { // preload sounds
const uint8 *soundsList = getSoundsList3DO(num);
for (int i = 0; soundsList[i] != 255; ++i) {
const int soundNum = soundsList[i];
loadDat(soundNum);
if (_memList[soundNum].status == STATUS_LOADED) {
preloadSound(data, soundNum, _memList[soundNum].bufPtr);
}
}
} else if (num >= 200) {
loadBmp(num);
}
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS:
if (num >= ENTRIES_COUNT_20TH)
error("Resource::update - Resource number too high %d", num);
MemEntry *me = &_memList[num];
if (me->status == STATUS_NULL) {
me->status = STATUS_TOLOAD;
load();
}
break;
}
}
void Resource::loadBmp(int num) {
uint32 size = 0;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadBmp(num);
break;
case DT_WIN31:
p = _win31->loadFile(num, nullptr, &size);
break;
case DT_3DO:
p = _3do->loadFile(num, nullptr, &size);
break;
default:
break;
}
if (p) {
_vid->copyBitmapPtr(p, size);
free(p);
}
}
uint8 *Resource::loadDat(int num) {
assert(num < _numMemList);
if (_memList[num].status == STATUS_LOADED) {
return _memList[num].bufPtr;
}
uint32 size = 0;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadDat(num, _scriptCurPtr, &size);
break;
case DT_WIN31:
p = _win31->loadFile(num, _scriptCurPtr, &size);
break;
case DT_3DO:
p = _3do->loadFile(num, _scriptCurPtr, &size);
break;
default:
break;
}
if (p) {
_scriptCurPtr += size;
_memList[num].bufPtr = p;
_memList[num].status = STATUS_LOADED;
}
return p;
}
void Resource::loadFont() {
if (_nth) {
uint8 *p = _nth->load("font.bmp");
if (p) {
_vid->setFont(p);
free(p);
}
}
}
void Resource::loadHeads() {
if (_nth) {
uint8 *p = _nth->load("heads.bmp");
if (p) {
_vid->setHeads(p);
free(p);
}
}
}
uint8 *Resource::loadWav(int num, uint32 *size) {
if (_memList[num].status == STATUS_LOADED) {
return _memList[num].bufPtr;
}
uint32 dummy = 0;
if (!size)
size = &dummy;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadWav(num, _scriptCurPtr, size);
break;
case DT_WIN31:
p = _win31->loadFile(num, _scriptCurPtr, size);
break;
default:
break;
}
if (p) {
_scriptCurPtr += *size;
_memList[num].bufPtr = p;
_memList[num].status = STATUS_LOADED;
}
return p;
}
const char *Resource::getString(int num) {
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
return _nth->getString(_lang, num);
case DT_WIN31:
return _win31->getString(num);
default:
break;
}
return nullptr;
}
const char *Resource::getMusicPath(int num, char *buf, int bufSize, uint32 *offset) {
const char *name = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
name = _nth->getMusicName(num);
break;
case DT_WIN31:
name = _win31->getMusicName(num);
break;
case DT_3DO:
assert(offset);
name = _3do->getMusicName(num, offset);
break;
default:
break;
}
if (name) {
snprintf(buf, bufSize, "%s", name);
return buf;
}
return nullptr;
}
const uint8 Resource::MEMLIST_PARTS[][4] = {
{ 0x14, 0x15, 0x16, 0x00 }, // 16000 - protection screens
{ 0x17, 0x18, 0x19, 0x00 }, // 16001 - introduction
{ 0x1A, 0x1B, 0x1C, 0x11 }, // 16002 - water
{ 0x1D, 0x1E, 0x1F, 0x11 }, // 16003 - jail
{ 0x20, 0x21, 0x22, 0x11 }, // 16004 - 'cite'
{ 0x23, 0x24, 0x25, 0x00 }, // 16005 - 'arene'
{ 0x26, 0x27, 0x28, 0x11 }, // 16006 - 'luxe'
{ 0x29, 0x2A, 0x2B, 0x11 }, // 16007 - 'final'
{ 0x7D, 0x7E, 0x7F, 0x00 }, // 16008 - password screen
{ 0x7D, 0x7E, 0x7F, 0x00 } // 16009 - password screen
};
void Resource::setupPart(int ptrId) {
int firstPart = kPartCopyProtection;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
case DT_3DO:
firstPart = kPartIntro;
/* fall-through */
case DT_WIN31:
if (ptrId >= firstPart && ptrId <= 16009) {
invalidateAll();
uint8 **segments[4] = { &_segVideoPal, &_segCode, &_segVideo1, &_segVideo2 };
for (int i = 0; i < 4; ++i) {
const int num = MEMLIST_PARTS[ptrId - 16000][i];
if (num != 0) {
*segments[i] = loadDat(num);
}
}
_currentPart = ptrId;
} else {
error("Resource::setupPart() ec=0x%X invalid part", 0xF07);
}
_scriptBakPtr = _scriptCurPtr;
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS:
if (ptrId != _currentPart) {
uint8 ipal = 0;
uint8 icod = 0;
uint8 ivd1 = 0;
uint8 ivd2 = 0;
if (ptrId >= 16000 && ptrId <= 16009) {
const uint16 part = ptrId - 16000;
ipal = MEMLIST_PARTS[part][0];
icod = MEMLIST_PARTS[part][1];
ivd1 = MEMLIST_PARTS[part][2];
ivd2 = MEMLIST_PARTS[part][3];
} else {
error("Resource::setupPart() ec=0x%X invalid part", 0xF07);
}
invalidateAll();
_memList[ipal].status = STATUS_TOLOAD;
_memList[icod].status = STATUS_TOLOAD;
_memList[ivd1].status = STATUS_TOLOAD;
if (ivd2 != 0) {
_memList[ivd2].status = STATUS_TOLOAD;
}
load();
_segVideoPal = _memList[ipal].bufPtr;
_segCode = _memList[icod].bufPtr;
_segVideo1 = _memList[ivd1].bufPtr;
if (ivd2 != 0) {
_segVideo2 = _memList[ivd2].bufPtr;
}
_currentPart = ptrId;
}
_scriptBakPtr = _scriptCurPtr;
break;
}
}
void Resource::allocMemBlock() {
_memPtrStart = (uint8 *)malloc(MEM_BLOCK_SIZE);
if (!_memPtrStart)
error("allocMemBlock - Error allocating memory");
_scriptBakPtr = _scriptCurPtr = _memPtrStart;
_vidCurPtr = _memPtrStart + MEM_BLOCK_SIZE - (320 * 200 / 2); // 4bpp bitmap
_useSegVideo2 = false;
}
void Resource::freeMemBlock() {
free(_memPtrStart);
_memPtrStart = nullptr;
}
void Resource::readDemo3Joy() {
static const char *filename = "demo3.joy";
Common::File f;
if (f.open(filename)) {
const uint32 fileSize = f.size();
_demo3Joy.bufPtr = (uint8 *)malloc(fileSize);
if (_demo3Joy.bufPtr) {
_demo3Joy.bufSize = f.read(_demo3Joy.bufPtr, fileSize);
_demo3Joy.bufPos = -1;
}
} else {
warning("Unable to open '%s'", filename);
}
}
void MemEntry::load(Common::SeekableReadStream *src) {
status = src->readByte();
type = src->readByte();
bufPtr = nullptr; (void)src->readUint32BE();
rankNum = src->readByte();
bankNum = src->readByte();
bankPos = src->readUint32BE();
packedSize = src->readUint32BE();
unpackedSize = src->readUint32BE();
}
} // namespace Awe

172
engines/awe/resource.h Normal file
View File

@@ -0,0 +1,172 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_RESOURCE_H
#define AWE_RESOURCE_H
#include "awe/detection.h"
#include "awe/intern.h"
namespace Awe {
struct MemEntry {
uint8 status = 0; // 0x0
uint8 type = 0; // 0x1, Resource::ResType
uint8 *bufPtr = nullptr; // 0x2
uint8 rankNum = 0; // 0x6
uint8 bankNum = 0; // 0x7
uint32 bankPos = 0; // 0x8
uint32 packedSize = 0; // 0xC
uint32 unpackedSize = 0; // 0x12
void load(Common::SeekableReadStream *src);
};
struct AmigaMemEntry {
uint8 type;
uint8 bank;
uint32 offset;
uint32 packedSize;
uint32 unpackedSize;
};
struct DemoJoy {
uint8 keymask = 0;
uint8 counter = 0;
uint8 *bufPtr = nullptr;
int bufPos = 0, bufSize = 0;
bool start() {
if (bufSize > 0) {
keymask = bufPtr[0];
counter = bufPtr[1];
bufPos = 2;
return true;
}
return false;
}
uint8 update() {
if (bufPos >= 0 && bufPos < bufSize) {
if (counter == 0) {
keymask = bufPtr[bufPos++];
counter = bufPtr[bufPos++];
} else {
--counter;
}
return keymask;
}
return 0;
}
};
struct ResourceNth;
struct ResourceWin31;
struct Resource3do;
struct Video;
typedef void (*PreloadSoundProc)(void *userdata, int num, const uint8 *data);
struct Resource {
enum ResType {
RT_SOUND = 0,
RT_MUSIC = 1,
RT_BITMAP = 2, // full screen 4bpp video buffer, size=200*320/2
RT_PALETTE = 3, // palette (1024=vga + 1024=ega), size=2048
RT_BYTECODE = 4,
RT_SHAPE = 5,
RT_BANK = 6, // common part shapes (bank2.mat)
};
enum {
MEM_BLOCK_SIZE = 1 * 1024 * 1024,
ENTRIES_COUNT = 146,
ENTRIES_COUNT_20TH = 178,
};
enum {
STATUS_NULL,
STATUS_LOADED,
STATUS_TOLOAD,
};
static const AmigaMemEntry MEMLIST_AMIGA_FR[ENTRIES_COUNT];
static const AmigaMemEntry MEMLIST_AMIGA_EN[ENTRIES_COUNT];
static const AmigaMemEntry MEMLIST_ATARI_EN[ENTRIES_COUNT];
static const uint8 MEMLIST_PARTS[][4];
static const AmigaMemEntry *detectAmigaAtari();
Video *_vid;
DataType _dataType;
MemEntry _memList[ENTRIES_COUNT_20TH];
uint16 _numMemList = 0;
uint16 _currentPart = 0, _nextPart = 0;
uint8 *_memPtrStart = nullptr,
*_scriptBakPtr = nullptr,
*_scriptCurPtr = nullptr,
*_vidCurPtr = nullptr;
bool _useSegVideo2 = false;
uint8 *_segVideoPal = nullptr;
uint8 *_segCode = nullptr;
uint8 *_segVideo1 = nullptr;
uint8 *_segVideo2 = nullptr;
const char *_bankPrefix = "bank";
bool _hasPasswordScreen = true;
ResourceNth *_nth = nullptr;
ResourceWin31 *_win31 = nullptr;
Resource3do *_3do = nullptr;
Language _lang = Language::EN_ANY;
const AmigaMemEntry *_amigaMemList;
DemoJoy _demo3Joy;
bool _copyProtection = false;
Resource(Video *vid, DataType dataType);
~Resource();
DataType getDataType() const {
return _dataType;
}
const char *getGameTitle(Language lang) const;
bool readBank(const MemEntry *me, uint8 *dstBuf);
void readEntries();
void readEntriesAmiga(const AmigaMemEntry *entries, int count);
void dumpEntries();
void load();
void invalidateAll();
void invalidateRes();
void update(uint16 num, PreloadSoundProc, void *);
void loadBmp(int num);
uint8 *loadDat(int num);
void loadFont();
void loadHeads();
uint8 *loadWav(int num, uint32 *size = nullptr);
const char *getString(int num);
const char *getMusicPath(int num, char *buf, int bufSize, uint32 *offset = nullptr);
void setupPart(int part);
void allocMemBlock();
void freeMemBlock();
void readDemo3Joy();
};
} // namespace Awe
#endif

View File

@@ -0,0 +1,199 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "awe/resource_3do.h"
namespace Awe {
static int decodeLzss(const uint8 *src, uint32 len, uint8 *dst) {
uint32 rd = 0, wr = 0;
while (rd < len) {
const uint8 code = src[rd++];
for (int j = 0; j < 8 && rd < len; ++j) {
if (code & (1 << j)) {
dst[wr++] = src[rd++];
} else {
const uint16 offset = 0xF000 | src[rd] | ((src[rd + 1] & 0xF) << 8);
const int llen = (src[rd + 1] >> 4) + 3;
rd += 2;
for (int i = 0; i < llen; ++i) {
dst[wr] = dst[wr + (int16)offset];
++wr;
}
}
}
}
return wr;
}
static void decodeCcb16(int ccbWidth, int ccbHeight, Common::File *f, uint32 dataSize, uint16 *dst) {
for (int y = 0; y < ccbHeight; ++y) {
const int scanlineSize = 4 * (f->readUint16BE() + 2);
int scanlineLen = 2;
int w = ccbWidth;
while (w > 0) {
uint8 code = f->readByte();
++scanlineLen;
const int count = (code & 63) + 1;
code >>= 6;
if (code == 0) {
break;
}
switch (code) {
case 1:
for (int i = 0; i < count; ++i) {
*dst++ = f->readUint16BE();
}
scanlineLen += count * 2;
break;
case 2:
memset(dst, 0, count * sizeof(uint16));
dst += count;
break;
case 3:
{
const uint16 color = f->readUint16BE();
for (int i = 0; i < count; ++i) {
*dst++ = color;
}
scanlineLen += 2;
}
break;
default:
break;
}
w -= count;
}
assert(w >= 0);
if (w > 0) {
dst += w;
}
const int align = scanlineSize - scanlineLen;
if (align != 0) {
f->seek(align, SEEK_CUR);
}
}
}
static const uint8 _ccb_bppTable[8] = {
0, 1, 2, 4, 6, 8, 16, 0
};
static uint16 *decodeShapeCcb(Common::File *f, int dataSize, int *w, int *h) {
// TODO: Can this reuse code from Image::Cel3DODecoder?
const uint32 flags = f->readUint32BE();
f->seek(4, SEEK_CUR);
const uint32 celData = f->readUint32BE();
f->seek(40, SEEK_CUR);
const uint32 pre0 = f->readUint32BE();
const uint32 pre1 = f->readUint32BE();
assert(celData == 0x30);
assert(flags & (1 << 9));
const int bpp = _ccb_bppTable[pre0 & 7];
assert(bpp == 16);
const uint32 width = (pre1 & 0x3FF) + 1;
const uint32 height = ((pre0 >> 6) & 0x3FF) + 1;
uint16 *buffer = (uint16 *)malloc(width * height * sizeof(uint16));
if (buffer) {
decodeCcb16(width, height, f, dataSize - 60, buffer);
*w = width;
*h = height;
}
return buffer;
}
bool Resource3do::readEntries() {
return true;
}
uint8 *Resource3do::loadFile(int num, uint8 *dst, uint32 *size) {
uint8 *in = dst;
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "GameData/File%d", num);
Common::File f;
if (f.open(path)) {
const int sz = f.size();
if (!dst) {
dst = (uint8 *)malloc(sz);
if (!dst) {
error("loadFile - Unable to allocate %d bytes", sz);
}
}
*size = sz;
f.read(dst, sz);
} else {
warning("Failed to load '%s'", path);
return nullptr;
}
if (memcmp(dst, "\x00\xf4\x01\x00", 4) == 0) {
static const int SZ = 64000 * 2;
uint8 *tmp = (uint8 *)calloc(1, SZ);
if (!tmp) {
error("loadFile - Unable to allocate %d bytes", SZ);
}
const int decodedSize = decodeLzss(dst + 4, *size - 4, tmp);
if (in != dst)
free(dst);
if (decodedSize != SZ) {
warning("Unexpected LZSS decoded size %d", decodedSize);
free(tmp);
*size = 0;
return nullptr;
}
*size = decodedSize;
return tmp;
}
return dst;
}
uint16 *Resource3do::loadShape555(const char *name, int *w, int *h) {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "GameData/%s", name);
Common::File f;
if (f.open(path)) {
const uint32 dataSize = f.size();
return decodeShapeCcb(&f, dataSize, w, h);
}
return nullptr;
}
const char *Resource3do::getMusicName(int num, uint32 *offset) {
*offset = 0;
snprintf(_musicPath, sizeof(_musicPath), "GameData/song%d", num);
return _musicPath;
}
const char *Resource3do::getCpak(const char *name, uint32 *offset) {
*offset = 0;
snprintf(_cpakPath, sizeof(_cpakPath), "GameData/%s", name);
return _cpakPath;
}
} // namespace Awe

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_RESOURCE_3DO_H
#define AWE_RESOURCE_3DO_H
#include "awe/intern.h"
namespace Awe {
struct Resource3do {
char _musicPath[32] = { '\0' };
char _cpakPath[64] = { '\0' };
Resource3do() {}
bool readEntries();
uint8 *loadFile(int num, uint8 *dst, uint32 *size);
uint16 *loadShape555(const char *name, int *w, int *h);
const char *getMusicName(int num, uint32 *offset);
const char *getCpak(const char *name, uint32 *offset);
};
} // namespace Awe
#endif

View File

@@ -0,0 +1,586 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/fs.h"
#include "awe/pak.h"
#include "awe/resource_nth.h"
#include "awe/script.h"
#include "awe/awe.h"
namespace Awe {
static char *loadTextFile(Common::File &f, const int size) {
char *buf = (char *)malloc(size + 1);
if (buf) {
const int count = f.read(buf, size);
if (count != size) {
warning("Failed to read %d bytes (%d expected)", count, size);
free(buf);
return nullptr;
}
buf[count] = 0;
}
return buf;
}
struct Resource15th : ResourceNth {
Pak _pak;
const char *_dataPath = "Data";
const char *_menuPath = "Menu";
char *_textBuf;
const char *_stringsTable[157];
bool _hasRemasteredMusic;
Resource15th() {
_hasRemasteredMusic = Common::FSNode("Music/AW/RmSnd").isDirectory();
_textBuf = nullptr;
memset(_stringsTable, 0, sizeof(_stringsTable));
}
virtual ~Resource15th() {
free(_textBuf);
}
virtual bool init() {
_pak.open(_dataPath);
_pak.readEntries();
return _pak._entriesCount != 0;
}
virtual uint8 *load(const char *name) {
uint8 *buf = nullptr;
const PakEntry *e = _pak.find(name);
if (e) {
buf = (uint8 *)malloc(e->size);
if (buf) {
uint32 size;
_pak.loadData(e, buf, &size);
}
} else {
warning("Unable to load '%s'", name);
}
return buf;
}
virtual uint8 *loadBmp(int num) {
char name[32];
if (num >= 3000) {
snprintf(name, sizeof(name), "e%04d.bmp", num);
} else {
snprintf(name, sizeof(name), "file%03d.bmp", num);
}
return load(name);
}
virtual uint8 *loadDat(int num, uint8 *dst, uint32 *size) {
char name[32];
snprintf(name, sizeof(name), "file%03d.dat", num);
const PakEntry *e = _pak.find(name);
if (e) {
_pak.loadData(e, dst, size);
return dst;
} else {
warning("Unable to load '%s'", name);
}
return nullptr;
}
virtual uint8 *loadWav(int num, uint8 *dst, uint32 *size) {
char name[32];
const PakEntry *e = nullptr;
if (Script::_useRemasteredAudio) {
snprintf(name, sizeof(name), "rmsnd/file%03d.wav", num);
e = _pak.find(name);
}
if (!e) {
snprintf(name, sizeof(name), "file%03db.wav", num);
e = _pak.find(name);
if (!e) {
snprintf(name, sizeof(name), "file%03d.wav", num);
e = _pak.find(name);
}
}
if (e) {
uint8 *p = (uint8 *)malloc(e->size);
if (p) {
_pak.loadData(e, p, size);
*size = 0;
return p;
}
warning("Failed to allocate %d bytes", e->size);
} else {
warning("Unable to load '%s'", name);
}
return nullptr;
}
void loadStrings(Language lang) {
if (!_textBuf) {
const char *name = nullptr;
switch (lang) {
case Common::FR_FRA:
name = "Francais.Txt";
break;
case Common::EN_ANY:
name = "English.Txt";
break;
case Common::DE_DEU:
name = "German.Txt";
break;
case Common::ES_ESP:
name = "Espanol.txt";
break;
case Common::IT_ITA:
name = "Italian.Txt";
break;
default:
return;
}
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/lang_%s", _menuPath, name);
Common::File f;
if (f.open(path)) {
const int size = f.size();
_textBuf = loadTextFile(f, size);
if (_textBuf) {
char *p = _textBuf;
while (true) {
char *end = strchr(p, '\r');
if (!end) {
break;
}
*end++ = 0;
if (*end == '\n') {
*end++ = 0;
}
const int len = end - p;
int strNum = -1;
if (len > 3 && sscanf(p, "%03d", &strNum) == 1) {
p += 3;
while (*p == ' ' || *p == '\t') {
++p;
}
if (strNum > 0 && (uint32)strNum < ARRAYSIZE(_stringsTable)) {
_stringsTable[strNum] = p;
}
}
p = end;
}
}
}
}
}
virtual const char *getString(Language lang, int num) {
loadStrings(lang);
if ((uint32)num < ARRAYSIZE(_stringsTable)) {
return _stringsTable[num];
}
return nullptr;
}
virtual const char *getMusicName(int num) {
const char *path = nullptr;
switch (num) {
case 7:
if (_hasRemasteredMusic && Script::_useRemasteredAudio) {
path = "Music/AW/RmSnd/Intro2004.wav";
} else {
path = "Music/AW/Intro2004.wav";
}
break;
case 138:
if (_hasRemasteredMusic && Script::_useRemasteredAudio) {
path = "Music/AW/RmSnd/End2004.wav";
} else {
path = "Music/AW/End2004.wav";
}
break;
default:
break;
}
return path;
}
virtual void getBitmapSize(int *w, int *h) {
*w = 1280;
*h = 800;
}
};
static uint8 *inflateGzip(const char *filepath) {
Common::File f;
if (!f.open(filepath)) {
warning("Unable to open '%s'", filepath);
return nullptr;
}
const uint16 sig = f.readUint16LE();
if (sig != 0x8B1F) {
warning("Unexpected file signature 0x%x for '%s'", sig, filepath);
return nullptr;
}
f.seek(-4, SEEK_END);
const uint32 dataSize = f.readUint32LE();
uint8 *out = (uint8 *)malloc(dataSize);
if (!out) {
warning("Failed to allocate %d bytes", dataSize);
return nullptr;
}
f.seek(0);
#ifdef TODO
z_stream str;
memset(&str, 0, sizeof(str));
int err = inflateInit2(&str, MAX_WBITS + 16);
if (err == Z_OK) {
Bytef buf[1 << MAX_WBITS];
str.next_in = buf;
str.avail_in = 0;
str.next_out = out;
str.avail_out = dataSize;
while (err == Z_OK && str.avail_out != 0) {
if (str.avail_in == 0 && !f.err()) {
str.next_in = buf;
str.avail_in = f.read(buf, sizeof(buf));
}
err = inflate(&str, Z_NO_FLUSH);
}
inflateEnd(&str);
if (err == Z_STREAM_END) {
return out;
}
}
free(out);
#else
error("TODO: inflateGzip");
#endif
return nullptr;
}
struct Resource20th : ResourceNth {
char *_textBuf;
const char *_stringsTable[192];
char _musicName[64];
uint8 _musicType;
char _datName[32];
const char *_bitmapSize;
Resource20th() : _textBuf(nullptr), _bitmapSize(nullptr) {
memset(_stringsTable, 0, sizeof(_stringsTable));
_musicType = 0;
_datName[0] = 0;
_musicName[0] = 0;
}
virtual ~Resource20th() {
free(_textBuf);
}
virtual bool init() {
static const char *dirs[] = { "BGZ", "DAT", "WGZ", nullptr };
for (int i = 0; dirs[i]; ++i) {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "game/%s", dirs[i]);
Common::FSNode f(path);
if (f.exists() && !f.isDirectory()) {
warning("'%s' is not a directory", path);
return false;
}
}
static const char *bmps[] = {
"1728x1080",
"1280x800",
"1152x720",
"960x600",
"864x540",
"768x480",
"480x300",
"320x200",
nullptr
};
_bitmapSize = nullptr;
for (int i = 0; bmps[i]; ++i) {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "game/BGZ/data%s", bmps[i]);
Common::FSNode f(path);
if (f.isDirectory()) {
_bitmapSize = bmps[i];
break;
}
}
return true;
}
virtual uint8 *load(const char *name) {
if (strcmp(name, "font.bmp") == 0) {
return inflateGzip("game/BGZ/Font.bgz");
} else if (strcmp(name, "heads.bmp") == 0) {
return inflateGzip("game/BGZ/Heads.bgz");
}
return nullptr;
}
virtual uint8 *loadBmp(int num) {
char path[MAXPATHLEN];
if (num >= 3000 && _bitmapSize) {
snprintf(path, sizeof(path), "game/BGZ/data%s/%s_e%04d.bgz", _bitmapSize, _bitmapSize, num);
} else {
snprintf(path, sizeof(path), "game/BGZ/file%03d.bgz", num);
}
return inflateGzip(path);
}
void preloadDat(int part, int type, int num) {
static const char *names[] = {
"INTRO", "EAU", "PRI", "CITE", "arene", "LUXE", "FINAL", nullptr
};
static const char *exts[] = {
"pal", "mac", "mat", nullptr
};
if (part > 0 && part < 8) {
if (type == 3) {
assert(num == 0x11);
Common::strcpy_s(_datName, "BANK2.MAT");
} else {
snprintf(_datName, sizeof(_datName), "%s2011.%s", names[part - 1], exts[type]);
}
debugC(kDebugResource, "Loading '%s'", _datName);
} else {
_datName[0] = 0;
}
}
virtual uint8 *loadDat(int num, uint8 *dst, uint32 *size) {
bool datOpen = false;
char path[MAXPATHLEN];
Common::strcpy_s(path, "game/DAT");
Common::File f;
if (_datName[0]) {
datOpen = f.open(_datName);
}
if (!datOpen) {
snprintf(_datName, sizeof(_datName), "FILE%03d.DAT", num);
datOpen = f.open(_datName);
}
if (datOpen) {
const uint32 dataSize = f.size();
const uint32 count = f.read(dst, dataSize);
if (count != dataSize) {
warning("Failed to read %d bytes (expected %d)", dataSize, count);
}
*size = dataSize;
} else {
warning("Unable to open '%s/%s'", path, _datName);
dst = nullptr;
}
_datName[0] = 0;
return dst;
}
virtual uint8 *loadWav(int num, uint8 *dst, uint32 *size) {
char path[MAXPATHLEN];
if (!Script::_useRemasteredAudio) {
snprintf(path, sizeof(path), "game/WGZ/original/file%03d.wgz", num);
if (!Common::File::exists(path)) {
snprintf(path, sizeof(path), "game/WGZ/original/file%03dB.wgz", num);
}
*size = 0;
return inflateGzip(path);
}
switch (num) {
case 81:
{
const int r = g_engine->getRandomNumber(1, 3);
snprintf(path, sizeof(path), "game/WGZ/file081-EX-%d.wgz", r);
}
break;
case 85:
{
const int r = g_engine->getRandomNumber(1, 2);
const char *snd = "IN";
if (_musicType == 1) {
snd = "EX";
}
snprintf(path, sizeof(path), "game/WGZ/file085-%s-%d.wgz", snd, r);
}
break;
case 96:
{
const int r = g_engine->getRandomNumber(1, 3);
const char *snd = "GR";
if (_musicType == 1) {
snd = "EX";
} else if (_musicType == 2) {
snd = "IN";
}
snprintf(path, sizeof(path), "game/WGZ/file096-%s-%d.wgz", snd, r);
}
break;
case 163:
{
const char *snd = "GR";
if (_musicType == 1) {
snd = "EX";
} else if (_musicType == 2) {
snd = "IN";
}
snprintf(path, sizeof(path), "game/WGZ/file163-%s-1.wgz", snd);
}
break;
default:
snprintf(path, sizeof(path), "game/WGZ/file%03d.wgz", num);
if (!Common::File::exists(path)) {
snprintf(path, sizeof(path), "game/WGZ/file%03dB.wgz", num);
}
break;
}
*size = 0;
return inflateGzip(path);
}
void loadStrings(Language lang) {
if (!_textBuf) {
const char *name = nullptr;
switch (lang) {
case Common::FR_FRA:
name = "FR";
break;
case Common::EN_ANY:
name = "EN";
break;
case Common::DE_DEU:
name = "DE";
break;
case Common::ES_ESP:
name = "ES";
break;
case Common::IT_ITA:
name = "IT";
break;
default:
return;
}
char path[MAXPATHLEN];
static const char *fmt[] = {
"game/TXT/%s.txt",
"game/TXT/Linux/%s.txt",
nullptr
};
bool isOpen = false;
Common::File f;
for (int i = 0; fmt[i] && !isOpen; ++i) {
snprintf(path, sizeof(path), fmt[i], name);
isOpen = f.open(path);
}
if (isOpen) {
const int size = f.size();
_textBuf = loadTextFile(f, size);
if (_textBuf) {
int count = 0;
for (char *p = _textBuf; count < (int)ARRAYSIZE(_stringsTable); ) {
_stringsTable[count++] = p;
char *end = strchr(p, '\n');
if (!end) {
break;
}
*end++ = 0;
p = end;
}
}
}
}
}
virtual const char *getString(Language lang, int num) {
loadStrings(lang);
if ((uint32)num < ARRAYSIZE(_stringsTable)) {
return _stringsTable[num];
}
return nullptr;
}
virtual const char *getMusicName(int num) {
if (num >= 5000 && Script::_useRemasteredAudio) {
snprintf(_musicName, sizeof(_musicName), "game/OGG/amb%d.ogg", num);
switch (num) {
case 5005:
_musicType = 1;
break;
case 5006:
_musicType = 3;
break;
default:
_musicType = 2;
break;
}
} else {
switch (num) {
case 7:
if (Script::_useRemasteredAudio) {
Common::strcpy_s(_musicName, "game/OGG/Intro_20th.ogg");
} else {
Common::strcpy_s(_musicName, "game/OGG/original/intro.ogg");
}
break;
case 138:
if (!Script::_useRemasteredAudio) {
Common::strcpy_s(_musicName, "game/OGG/original/ending.ogg");
break;
}
/* fall-through */
default:
return nullptr;
}
}
return _musicName;
}
virtual void getBitmapSize(int *w, int *h) {
if (_bitmapSize && sscanf(_bitmapSize, "%dx%d", w, h) == 2) {
return;
}
*w = 0;
*h = 0;
}
};
ResourceNth *ResourceNth::create(int edition) {
switch (edition) {
case 15:
return new Resource15th();
case 20:
return new Resource20th();
default:
break;
}
return nullptr;
}
} // namespace Awe

View File

@@ -0,0 +1,48 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_RESOURCE_NTH_H
#define AWE_RESOURCE_NTH_H
#include "awe/intern.h"
namespace Awe {
struct ResourceNth {
virtual ~ResourceNth() {
}
virtual bool init() = 0;
virtual uint8 *load(const char *name) = 0;
virtual uint8 *loadBmp(int num) = 0;
virtual void preloadDat(int part, int type, int num) {}
virtual uint8 *loadDat(int num, uint8 *dst, uint32 *size) = 0;
virtual uint8 *loadWav(int num, uint8 *dst, uint32 *size) = 0;
virtual const char *getString(Language lang, int num) = 0;
virtual const char *getMusicName(int num) = 0;
virtual void getBitmapSize(int *w, int *h) = 0;
static ResourceNth *create(int edition);
};
} // namespace Awe
#endif

View File

@@ -0,0 +1,366 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/resource_win31.h"
#include "awe/detection.h"
namespace Awe {
static const uint8 _shuffleTable[256] = {
0xB2, 0x91, 0x49, 0xEE, 0x8C, 0xBC, 0x16, 0x0D, 0x07, 0x87, 0xCD, 0xB6, 0x4C, 0x44, 0x22, 0xB3,
0xAE, 0x96, 0xDF, 0x18, 0x7B, 0x28, 0x17, 0x9A, 0x74, 0x3C, 0x2E, 0x59, 0x69, 0x56, 0x38, 0x82,
0x7F, 0x25, 0x41, 0xC6, 0xE8, 0x8A, 0x86, 0x7A, 0xB5, 0x8B, 0xA7, 0xB1, 0x2C, 0x53, 0xF0, 0x3B,
0x20, 0xCB, 0x6F, 0x9E, 0xD9, 0x05, 0x54, 0x08, 0x4F, 0xFE, 0x32, 0x31, 0xF9, 0x50, 0xBD, 0x37,
0x45, 0xDA, 0x46, 0x33, 0x01, 0xC5, 0x27, 0xEC, 0xE5, 0x14, 0x98, 0x70, 0xB0, 0xF8, 0x93, 0xC9,
0xAC, 0xEB, 0xE4, 0xE1, 0xE6, 0xF7, 0xAF, 0x76, 0x0E, 0x63, 0x80, 0x83, 0x1E, 0x57, 0x47, 0x9F,
0xC2, 0x42, 0xA5, 0xFF, 0x5B, 0xBF, 0x12, 0xFA, 0x61, 0x5E, 0x5D, 0xC8, 0x21, 0xA8, 0xB9, 0x5A,
0x9D, 0x30, 0xD5, 0x09, 0xB7, 0x0B, 0x2F, 0xED, 0x6E, 0xA2, 0x5F, 0x6C, 0xA0, 0x95, 0x00, 0x55,
0x75, 0x7D, 0x89, 0x97, 0x6A, 0xFB, 0x1A, 0x58, 0xDE, 0x8D, 0x4E, 0xE3, 0x4B, 0x3D, 0x15, 0x67,
0x11, 0x5C, 0x1C, 0x71, 0x73, 0x1B, 0xD3, 0x13, 0xE7, 0x77, 0x4D, 0xD6, 0x9C, 0x1D, 0x1F, 0xEF,
0xBB, 0x66, 0x99, 0xF6, 0x3F, 0x02, 0x7E, 0xCF, 0x2B, 0x35, 0x88, 0xBA, 0xA4, 0x40, 0x19, 0x23,
0xC1, 0xD4, 0xD7, 0x43, 0x52, 0x34, 0xE9, 0xDC, 0x60, 0x24, 0x94, 0x6B, 0x81, 0x03, 0xC0, 0x39,
0xBE, 0x90, 0x65, 0xFD, 0xE0, 0x2D, 0x7C, 0xEA, 0x04, 0xA6, 0xDB, 0xF3, 0xCE, 0xB4, 0xA9, 0xAA,
0xAD, 0x64, 0xF2, 0x72, 0xD2, 0x84, 0x8E, 0xD1, 0x26, 0xA3, 0xCA, 0x4A, 0x48, 0x06, 0x0F, 0x36,
0x85, 0xD0, 0x51, 0x6D, 0xC4, 0x3E, 0x92, 0xF1, 0xC7, 0x62, 0x79, 0xA1, 0x9B, 0x68, 0xF5, 0xE2,
0xAB, 0x0C, 0xCC, 0x78, 0xFC, 0x2A, 0xD8, 0x3A, 0xDD, 0x8F, 0x10, 0x29, 0xF4, 0x0A, 0xB8, 0xC3
};
static uint16 decode(uint8 *p, int size, uint16 key) {
for (int i = 0; i < size; ++i) {
const uint8 dl = 1 + (key >> 8);
const uint8 al = _shuffleTable[dl];
const uint8 dh = al ^ (key & 255);
p[i] ^= al;
key = (dh << 8) | dl;
}
return key;
}
struct Bitstream {
Common::File *_f;
int _size;
uint16 _bits;
int _len;
Bitstream()
: _f(nullptr), _size(0), _bits(0), _len(0) {
}
void reset(Common::File *f, int size) {
_f = f;
_size = size;
_bits = 0;
_len = 0;
}
uint8 readByte() {
if (_len < 8) {
_bits <<= 8;
assert(_size > 0);
--_size;
_bits |= _f->readByte();
_len += 8;
}
_len -= 8;
return (_bits >> _len) & 255;
}
int readBit() {
if (_len == 0) {
assert(_size > 0);
--_size;
_bits = _f->readByte();
_len = 8;
}
--_len;
return (_bits & (1 << _len)) != 0 ? 1 : 0;
}
};
struct LzHuffman {
enum {
kCharsCount = 314,
kTableSize = kCharsCount * 2 - 1,
kHuffmanRoot = kTableSize - 1,
kMaxFreq = 0x8000
};
Bitstream _stream;
int _child[kTableSize];
int _freq[628];
int _parent[943];
unsigned char _historyBuffer[4096];
LzHuffman() {
memset(_child, 0, sizeof(_child));
memset(_freq, 0, sizeof(_freq));
memset(_parent, 0, sizeof(_parent));
memset(_historyBuffer, 0, sizeof(_historyBuffer));
}
void resetHuffTables() {
for (int i = 0; i < kCharsCount; ++i) {
_freq[i] = 1;
_child[i] = kTableSize + i;
_parent[kTableSize + i] = i;
}
for (int i = 0, j = kCharsCount; j <= kHuffmanRoot; i += 2, ++j) {
_freq[j] = _freq[i] + _freq[i + 1];
_child[j] = i;
_parent[i] = _parent[i + 1] = j;
}
_freq[kTableSize] = 0xFFFF;
_parent[kHuffmanRoot] = 0;
}
int getHuffCode() {
static const int base[] = { 0, 1, 4, 12, 24, 48 };
static const int count[] = { 0, 2, 5, 9, 12, 15 };
static const int length[] = { 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5 };
int index = _stream.readByte();
const int len = length[index >> 4];
const int code = base[len] + (index - count[len] * 16) / (1 << (5 - len));
for (int i = 0; i <= len; ++i) {
index = (index << 1) | _stream.readBit();
}
return (index & 63) | (code << 6);
}
int decodeChar() {
int i = _child[kHuffmanRoot];
while (i < kTableSize) {
i += _stream.readBit();
if (i < kTableSize)
i = _child[i];
}
i -= kTableSize;
update(i);
return i;
}
void update(int num) {
if (_freq[kHuffmanRoot] == kMaxFreq) {
for (int j = 0, i = 0; j < kTableSize; ++j) {
if (_child[j] >= kTableSize) {
_freq[i] = (_freq[j] + 1) >> 1;
_child[i] = _child[j];
++i;
}
}
for (int j = 0, i = kCharsCount; i < kTableSize; j += 2, ++i) {
const int f = _freq[i] = _freq[j] + _freq[j + 1];
int index = i - 1;
while (_freq[index] > f) {
--index;
}
++index;
const int copySize = (i - index) * sizeof(int);
memmove(_freq + index + 1, _freq + index, copySize);
_freq[index] = f;
if (index + 1 < kTableSize)
memmove(_child + index + 1, _child + index, copySize);
_child[index] = j;
}
for (int i = 0; i < kTableSize; ++i) {
const int j = _child[i];
if (j >= kTableSize) {
_parent[j] = i;
} else {
_parent[j] = _parent[j + 1] = i;
}
}
}
int p = _parent[kTableSize + num];
do {
++_freq[p];
const int i = _freq[p];
int index = p + 1;
if (_freq[index] < i) {
while (_freq[++index] < i) {
}
--index;
_freq[p] = _freq[index];
_freq[index] = i;
const int k = _child[p];
_parent[k] = index;
if (k < kTableSize) {
_parent[k + 1] = index;
}
const int j = _child[index];
_child[index] = k;
_parent[j] = p;
if (j < kTableSize) {
_parent[j + 1] = p;
}
_child[p] = j;
p = index;
}
p = _parent[p];
} while (p != 0);
}
bool decode(uint8 *out, int uncompressedSize) {
resetHuffTables();
memset(_historyBuffer, ' ', sizeof(_historyBuffer));
int offset = 4078;
int currentSize = 0;
while (currentSize < uncompressedSize) {
int chr = decodeChar();
if (chr < 256) {
*out++ = chr & 255;
_historyBuffer[offset++] = chr;
offset &= 0xFFF;
++currentSize;
} else {
const int baseOffset = (offset - getHuffCode() - 1) & 0xFFF;
const int size = chr - 253;
for (int i = 0; i < size; ++i) {
chr = _historyBuffer[(baseOffset + i) & 0xFFF];
*out++ = chr & 255;
_historyBuffer[offset++] = chr;
offset &= 0xFFF;
++currentSize;
}
}
}
return currentSize == uncompressedSize;
}
bool decompressEntry(Common::File &f, const Win31BankEntry *e, uint8 *out) {
f.seek(e->offset);
_stream.reset(&f, e->packedSize);
return decode(out, e->size);
}
};
const char *ResourceWin31::FILENAME = "BANK";
ResourceWin31::ResourceWin31() {
if (!_f.open(FILENAME))
error("Could not open BANK");
}
ResourceWin31::~ResourceWin31() {
free(_entries);
free(_textBuf);
}
bool ResourceWin31::readEntries() {
uint8 buf[32];
const int count = _f.read(buf, sizeof(buf));
if (count == 32 && memcmp(buf, "NL\00\00", 4) == 0) {
_entriesCount = READ_LE_UINT16(buf + 4);
debugC(kDebugResource, "Read %d entries in win31 '%s'", _entriesCount, FILENAME);
_entries = (Win31BankEntry *)calloc(_entriesCount, sizeof(Win31BankEntry));
if (_entries) {
uint16 key = READ_LE_UINT16(buf + 0x14);
for (int i = 0; i < _entriesCount; ++i) {
_f.read(buf, sizeof(buf));
key = decode(buf, sizeof(buf), key);
Win31BankEntry *e = &_entries[i];
memcpy(e->name, buf, 16);
const uint16 flags = READ_LE_UINT16(buf + 16);
e->type = buf[19];
e->size = READ_LE_UINT32(buf + 20);
e->offset = READ_LE_UINT32(buf + 24);
e->packedSize = READ_LE_UINT32(buf + 28);
debugC(kDebugResource, "Res #%03d '%s' type %d size %d (%d) offset 0x%x", i, e->name, e->type, e->size, e->packedSize, e->offset);
assert(e->size == 0 || flags == 0x80);
}
readStrings();
}
}
return _entries != nullptr;
}
uint8 *ResourceWin31::loadFile(int num, uint8 *dst, uint32 *size) {
bool allocated = false;
if (num > 0 && num < _entriesCount) {
Win31BankEntry *e = &_entries[num];
*size = e->size;
if (!dst) {
allocated = true;
dst = (uint8 *)malloc(e->size);
if (!dst) {
warning("Unable to allocate %d bytes", e->size);
return nullptr;
}
}
// check for unpacked data
char name[32];
snprintf(name, sizeof(name), "%03d_%s", num, e->name);
Common::File f;
if (f.open(name) && f.size() == e->size) {
f.read(dst, e->size);
return dst;
}
LzHuffman lzHuf;
if (lzHuf.decompressEntry(_f, e, dst)) {
return dst;
}
}
warning("Unable to load resource #%d", num);
if (allocated)
free(dst);
return nullptr;
}
void ResourceWin31::readStrings() {
uint32 len, offset = 0;
_textBuf = loadFile(148, nullptr, &len);
while (true) {
const uint32 sep = READ_LE_UINT32(_textBuf + offset); offset += 4;
const uint16 num = sep >> 16;
if (num == 0xFFFF) {
break;
}
if (num < ARRAYSIZE(_stringsTable) && _stringsTable[num] == nullptr) {
_stringsTable[num] = (const char *)_textBuf + offset;
}
while (offset < len && _textBuf[offset++] != 0)
;
// strings are not always '\0' terminated
if (_textBuf[offset + 1] != 0) {
--offset;
}
}
}
const char *ResourceWin31::getString(int num) const {
return _stringsTable[num];
}
const char *ResourceWin31::getMusicName(int num) const {
switch (num) {
case 7:
return "y.mid";
case 138:
return "X.mid";
default:
break;
}
return nullptr;
}
} // namespace Awe

View File

@@ -0,0 +1,60 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_RESOURCE_WIN31_H
#define AWE_RESOURCE_WIN31_H
#include "common/file.h"
#include "awe/intern.h"
namespace Awe {
struct Win31BankEntry {
char name[16] = { '\0' };
uint8 type = 0;
uint32 offset = 0;
uint32 size = 0;
uint32 packedSize = 0;
};
struct ResourceWin31 {
static const char *FILENAME;
Common::File _f;
Win31BankEntry *_entries = nullptr;
int _entriesCount = 0;
uint8 *_textBuf = nullptr;
const char *_stringsTable[614] = { nullptr };
ResourceWin31();
~ResourceWin31();
bool readEntries();
uint8 *loadFile(int num, uint8 *dst, uint32 *size);
void readStrings();
const char *getString(int num) const;
const char *getMusicName(int num) const;
};
} // namespace Awe
#endif

927
engines/awe/script.cpp Normal file
View File

@@ -0,0 +1,927 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/gfx.h"
#include "awe/script.h"
#include "awe/resource.h"
#include "awe/video.h"
#include "awe/sfx_player.h"
#include "awe/sound.h"
#include "awe/system_stub.h"
namespace Awe {
Script::Script(Sound *snd, Resource *res, SfxPlayer *ply, Video *vid)
: _sound(snd), _res(res), _ply(ply), _vid(vid), _stub(nullptr) {
}
void Script::init() {
memset(_scriptVars, 0, sizeof(_scriptVars));
_fastMode = false;
_ply->_syncVar = &_scriptVars[VAR_MUSIC_SYNC];
_scriptPtr.byteSwap = _is3DO = (_res->getDataType() == DT_3DO);
if (_is3DO) {
_scriptVars[0xDB] = 1;
_scriptVars[0xE2] = 1;
_scriptVars[0xF2] = 6000;
} else if (_res->getDataType() != DT_15TH_EDITION && _res->getDataType() != DT_20TH_EDITION) {
_scriptVars[VAR_RANDOM_SEED] = 0; // time(0);
if (!_res->_copyProtection) {
// these 3 variables are set by the game code
_scriptVars[0xBC] = 0x10;
_scriptVars[0xC6] = 0x80;
_scriptVars[0xF2] = (_res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI) ? 6000 : 4000;
// these 2 variables are set by the engine executable
_scriptVars[0xDC] = 33;
}
if (_res->getDataType() == DT_DOS || _res->getDataType() == DT_WIN31) {
_scriptVars[0xE4] = 20;
}
}
}
void Script::op_movConst() {
const uint8 i = _scriptPtr.fetchByte();
const int16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_movConst(0x%02X, %d)", i, n);
_scriptVars[i] = n;
}
void Script::op_mov() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_mov(0x%02X, 0x%02X)", i, j);
_scriptVars[i] = _scriptVars[j];
}
void Script::op_add() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_add(0x%02X, 0x%02X)", i, j);
_scriptVars[i] += _scriptVars[j];
}
void Script::op_addConst() {
if (_res->getDataType() == DT_DOS || _res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI) {
if (_res->_currentPart == 16006 && _scriptPtr.pc == _res->_segCode + 0x6D48) {
warning("Script::op_addConst() workaround for infinite looping gun sound");
// The script 0x27 slot 0x17 doesn't stop the gun sound from looping.
// This is a bug in the original game code, confirmed by Eric Chahi and
// addressed with the anniversary editions.
// For older releases (DOS, Amiga), we play the 'stop' sound like it is
// done in other part of the game code.
//
// 6D43: jmp(0x6CE5)
// 6D46: break
// 6D47: VAR(0x06) -= 50
//
snd_playSound(0x5B, 1, 64, 1);
}
}
const uint8 i = _scriptPtr.fetchByte();
const int16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_addConst(0x%02X, %d)", i, n);
_scriptVars[i] += n;
}
void Script::op_call() {
const uint16 off = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_call(0x%X)", off);
if (_stackPtr == 0x40) {
error("Script::op_call() ec=0x%X stack overflow", 0x8F);
}
_scriptStackCalls[_stackPtr] = _scriptPtr.pc - _res->_segCode;
++_stackPtr;
_scriptPtr.pc = _res->_segCode + off;
}
void Script::op_ret() {
debugC(kDebugScript, "Script::op_ret()");
if (_stackPtr == 0) {
error("Script::op_ret() ec=0x%X stack underflow", 0x8F);
}
--_stackPtr;
_scriptPtr.pc = _res->_segCode + _scriptStackCalls[_stackPtr];
}
void Script::op_yieldTask() {
debugC(kDebugScript, "Script::op_yieldTask()");
_scriptPaused = true;
}
void Script::op_jmp() {
const uint16 off = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_jmp(0x%02X)", off);
_scriptPtr.pc = _res->_segCode + off;
}
void Script::op_installTask() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_installTask(0x%X, 0x%X)", i, n);
assert(i < 0x40);
_scriptTasks[1][i] = n;
}
void Script::op_jmpIfVar() {
const uint8 i = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_jmpIfVar(0x%02X)", i);
--_scriptVars[i];
if (_scriptVars[i] != 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
void Script::op_condJmp() {
// Script patch the original interpreter triggers
// if an incorrect code is entered at the start
if (_res->_currentPart == kPartCopyProtection &&
_res->_dataType == DT_DOS &&
(_scriptPtr.pc - _res->_segCode) == 0xc4c) {
byte *script = _scriptPtr.pc;
*script = 0x81;
WRITE_BE_UINT16(script + 3, 0xcb7);
WRITE_BE_UINT16(script + 153, 0xced);
}
const uint8 op = _scriptPtr.fetchByte();
const uint8 var = _scriptPtr.fetchByte();
const int16 b = _scriptVars[var];
int16 a;
if (op & 0x80) {
a = _scriptVars[_scriptPtr.fetchByte()];
} else if (op & 0x40) {
a = _scriptPtr.fetchWord();
} else {
a = _scriptPtr.fetchByte();
}
debugC(kDebugScript, "Script::op_condJmp(%d, 0x%02X, 0x%02X) var=0x%02X", op, b, a, var);
bool expr = false;
switch (op & 7) {
case 0:
expr = (b == a);
if (!_res->_copyProtection) {
if (_res->_currentPart == kPartCopyProtection) {
//
// 0CB8: jmpIf(VAR(0x29) == VAR(0x1E), @0CD3)
// ...
//
if (var == 0x29 && (op & 0x80) != 0) {
// 4 symbols
_scriptVars[0x29] = _scriptVars[0x1E];
_scriptVars[0x2A] = _scriptVars[0x1F];
_scriptVars[0x2B] = _scriptVars[0x20];
_scriptVars[0x2C] = _scriptVars[0x21];
// counters
_scriptVars[0x32] = 6;
_scriptVars[0x64] = 20;
warning("Script::op_condJmp() bypassing protection");
expr = true;
}
}
}
break;
case 1:
expr = (b != a);
break;
case 2:
expr = (b > a);
break;
case 3:
expr = (b >= a);
break;
case 4:
expr = (b < a);
break;
case 5:
expr = (b <= a);
break;
default:
warning("Script::op_condJmp() invalid condition %d", (op & 7));
break;
}
if (expr) {
op_jmp();
if (!_is3DO && var == VAR_SCREEN_NUM && _screenNum != _scriptVars[VAR_SCREEN_NUM]) {
fixUpPalette_changeScreen(_res->_currentPart, _scriptVars[VAR_SCREEN_NUM]);
_screenNum = _scriptVars[VAR_SCREEN_NUM];
}
} else {
_scriptPtr.fetchWord();
}
}
void Script::op_setPalette() {
const uint16 i = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_changePalette(%d)", i);
const int num = i >> 8;
if (_vid->_graphics->_fixUpPalette == FIXUP_PALETTE_REDRAW) {
if (_res->_currentPart == 16001) {
if (num == 10 || num == 16) {
return;
}
}
_vid->_nextPal = num;
} else {
_vid->_nextPal = num;
}
}
void Script::op_changeTasksState() {
uint8 start = _scriptPtr.fetchByte();
const uint8 end = _scriptPtr.fetchByte();
if (end < start) {
warning("Script::op_changeTasksState() ec=0x%X (end < start)", 0x880);
return;
}
const uint8 state = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_changeTasksState(%d, %d, %d)", start, end, state);
if (state == 2) {
for (; start <= end; ++start) {
_scriptTasks[1][start] = 0xFFFE;
}
} else if (state < 2) {
for (; start <= end; ++start) {
_scriptStates[1][start] = state;
}
}
}
void Script::op_selectPage() {
const uint8 i = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_selectPage(%d)", i);
_vid->setWorkPagePtr(i);
}
void Script::op_fillPage() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 color = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_fillPage(%d, %d)", i, color);
_vid->fillPage(i, color);
}
void Script::op_copyPage() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_copyPage(%d, %d)", i, j);
_vid->copyPage(i, j, _scriptVars[VAR_SCROLL_Y]);
}
void Script::op_updateDisplay() {
const uint8 page = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_updateDisplay(%d)", page);
inp_handleSpecialKeys();
if (_res->_copyProtection) {
// entered protection symbols match the expected values
if (_res->_currentPart == kPartCopyProtection && _scriptVars[0x67] == 1) {
_scriptVars[0xDC] = 33;
}
}
const int frameHz = _is3DO ? 60 : 50;
if (!_fastMode && _scriptVars[VAR_PAUSE_SLICES] != 0) {
const int delay = _stub->getTimeStamp() - _timeStamp;
const int pause = _scriptVars[VAR_PAUSE_SLICES] * 1000 / frameHz - delay;
if (pause > 0) {
_stub->sleep(pause);
}
}
_timeStamp = _stub->getTimeStamp();
if (_is3DO) {
_scriptVars[0xF7] = (_timeStamp - _startTime) * frameHz / 1000;
} else {
_scriptVars[0xF7] = 0;
}
_vid->_displayHead = !((_res->_currentPart == 16004 && _screenNum == 37) || (_res->_currentPart == 16006 && _screenNum == 202));
_vid->updateDisplay(page, _stub);
}
void Script::op_removeTask() {
debugC(kDebugScript, "Script::op_removeTask()");
_scriptPtr.pc = _res->_segCode + 0xFFFF;
_scriptPaused = true;
}
void Script::op_drawString() {
const uint16 strId = _scriptPtr.fetchWord();
const uint16 x = _scriptPtr.fetchByte();
const uint16 y = _scriptPtr.fetchByte();
const uint16 col = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_drawString(0x%03X, %d, %d, %d)", strId, x, y, col);
_vid->drawString(col, x, y, strId);
}
void Script::op_sub() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_sub(0x%02X, 0x%02X)", i, j);
_scriptVars[i] -= _scriptVars[j];
}
void Script::op_and() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_and(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] & n;
}
void Script::op_or() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_or(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] | n;
}
void Script::op_shl() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_shl(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] << n;
}
void Script::op_shr() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_shr(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] >> n;
}
void Script::op_playSound() {
const uint16 resNum = _scriptPtr.fetchWord();
const uint8 freq = _scriptPtr.fetchByte();
const uint8 vol = _scriptPtr.fetchByte();
const uint8 channel = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
snd_playSound(resNum, freq, vol, channel);
}
static void preloadSoundCb(void *userdata, int soundNum, const uint8 *data) {
((Script *)userdata)->snd_preloadSound(soundNum, data);
}
void Script::op_updateResources() {
const uint16 num = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_updateResources(%d)", num);
if (num == 0) {
_ply->stop();
_sound->stopAll();
_res->invalidateRes();
} else {
_res->update(num, preloadSoundCb, this);
}
}
void Script::op_playMusic() {
const uint16 resNum = _scriptPtr.fetchWord();
const uint16 delay = _scriptPtr.fetchWord();
const uint8 pos = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_playMusic(0x%X, %d, %d)", resNum, delay, pos);
snd_playMusic(resNum, delay, pos);
}
void Script::restartAt(int part, int pos) {
_ply->stop();
_sound->stopAll();
if (_res->getDataType() == DT_20TH_EDITION) {
_scriptVars[0xBF] = _difficulty; // difficulty (0 to 2)
// _scriptVars[0xDB] = 1; // preload sounds (resnum >= 2000)
_scriptVars[0xDE] = _useRemasteredAudio ? 1 : 0; // playback remastered sounds (resnum >= 146)
}
if (_res->getDataType() == DT_DOS && part == kPartCopyProtection) {
// VAR(0x54) indicates if the "Out of this World" title screen should be presented
//
// 0084: jmpIf(VAR(0x54) < 128, @00C4)
// ..
// 008D: setPalette(num=0)
// 0090: updateResources(res=18)
// ...
// 00C4: setPalette(num=23)
// 00CA: updateResources(res=71)
// Use "Out of this World" title screen if playing the USA release
const bool awTitleScreen = !(_res->_lang == Common::EN_USA);
_scriptVars[0x54] = awTitleScreen ? 0x1 : 0x81;
}
_res->setupPart(part);
memset(_scriptTasks, 0xFF, sizeof(_scriptTasks));
memset(_scriptStates, 0, sizeof(_scriptStates));
_scriptTasks[0][0] = 0;
_screenNum = -1;
if (pos >= 0) {
_scriptVars[0] = pos;
}
_startTime = _timeStamp = _stub->getTimeStamp();
if (part == kPartWater) {
if (_res->_demo3Joy.start()) {
memset(_scriptVars, 0, sizeof(_scriptVars));
}
}
}
void Script::setupTasks() {
if (_res->_nextPart != 0) {
restartAt(_res->_nextPart);
_res->_nextPart = 0;
}
for (int i = 0; i < 0x40; ++i) {
_scriptStates[0][i] = _scriptStates[1][i];
const uint16 n = _scriptTasks[1][i];
if (n != 0xFFFF) {
_scriptTasks[0][i] = (n == 0xFFFE) ? 0xFFFF : n;
_scriptTasks[1][i] = 0xFFFF;
}
}
}
void Script::runTasks() {
for (int i = 0; i < 0x40 && !_stub->_pi.quit; ++i) {
if (_scriptStates[0][i] == 0) {
const uint16 n = _scriptTasks[0][i];
if (n != 0xFFFF) {
_scriptPtr.pc = _res->_segCode + n;
_stackPtr = 0;
_scriptPaused = false;
debugC(kDebugScript, "Script::runTasks() i=0x%02X n=0x%02X", i, n);
executeTask();
_scriptTasks[0][i] = _scriptPtr.pc - _res->_segCode;
debugC(kDebugScript, "Script::runTasks() i=0x%02X pos=0x%X", i, _scriptTasks[0][i]);
}
}
}
}
void Script::executeTask() {
while (!_scriptPaused) {
const uint8 opcode = _scriptPtr.fetchByte();
if (opcode & 0x80) {
const uint16 off = ((opcode << 8) | _scriptPtr.fetchByte()) << 1;
_res->_useSegVideo2 = false;
Point pt;
pt.x = _scriptPtr.fetchByte();
pt.y = _scriptPtr.fetchByte();
const int16 h = pt.y - 199;
if (h > 0) {
pt.y = 199;
pt.x += h;
}
debugC(kDebugVideo, "vid_opcd_0x80 : opcode=0x%X off=0x%X x=%d y=%d", opcode, off, pt.x, pt.y);
_vid->setDataBuffer(_res->_segVideo1, off);
if (_is3DO) {
_vid->drawShape3DO(0xFF, 64, &pt);
} else {
_vid->drawShape(0xFF, 64, &pt);
}
} else if (opcode & 0x40) {
Point pt;
const uint8 offsetHi = _scriptPtr.fetchByte();
const uint16 off = ((offsetHi << 8) | _scriptPtr.fetchByte()) << 1;
pt.x = _scriptPtr.fetchByte();
_res->_useSegVideo2 = false;
if (!(opcode & 0x20)) {
if (!(opcode & 0x10)) {
pt.x = (pt.x << 8) | _scriptPtr.fetchByte();
} else {
pt.x = _scriptVars[pt.x];
}
} else {
if (opcode & 0x10) {
pt.x += 0x100;
}
}
pt.y = _scriptPtr.fetchByte();
if (!(opcode & 8)) {
if (!(opcode & 4)) {
pt.y = (pt.y << 8) | _scriptPtr.fetchByte();
} else {
pt.y = _scriptVars[pt.y];
}
}
uint16 zoom = 64;
if (!(opcode & 2)) {
if (opcode & 1) {
zoom = _scriptVars[_scriptPtr.fetchByte()];
}
} else {
if (opcode & 1) {
_res->_useSegVideo2 = true;
} else {
zoom = _scriptPtr.fetchByte();
}
}
debugC(kDebugVideo, "vid_opcd_0x40 : off=0x%X x=%d y=%d", off, pt.x, pt.y);
_vid->setDataBuffer(_res->_useSegVideo2 ? _res->_segVideo2 : _res->_segVideo1, off);
if (_is3DO) {
_vid->drawShape3DO(0xFF, zoom, &pt);
} else {
_vid->drawShape(0xFF, zoom, &pt);
}
} else {
if (_is3DO) {
switch (opcode) {
case 11:
{
const int num = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op11() setPalette %d", num);
_vid->changePal(num);
}
continue;
case 22:
{
const int var = _scriptPtr.fetchByte();
const int shift = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op22() VAR(0x%02X) <<= %d", var, shift);
_scriptVars[var] = (uint16)_scriptVars[var] << shift;
}
continue;
case 23:
{
const int var = _scriptPtr.fetchByte();
const int shift = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op23() VAR(0x%02X) >>= %d", var, shift);
_scriptVars[var] = (uint16)_scriptVars[var] >> shift;
}
continue;
case 26:
{
const int num = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op26() playMusic %d", num);
snd_playMusic(num, 0, 0);
}
continue;
case 27:
{
const int num = _scriptPtr.fetchWord();
const int x = _scriptVars[_scriptPtr.fetchByte()];
const int y = _scriptVars[_scriptPtr.fetchByte()];
const int color = _scriptPtr.fetchByte();
_vid->drawString(color, x, y, num);
}
continue;
case 28:
{
const uint8 var = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op28() jmpIf(VAR(0x%02x) == 0)", var);
if (_scriptVars[var] == 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
continue;
case 29:
{
const uint8 var = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op29() jmpIf(VAR(0x%02x) != 0)", var);
if (_scriptVars[var] != 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
continue;
case 30:
{
::debug("Time = %d", _scriptVars[0xF7]);
}
continue;
default:
break;
}
}
if (opcode > 0x1A)
error("Script::executeTask() ec=0x%X invalid opcode=0x%X", 0xFFF, opcode);
(this->*OPCODE_TABLE[opcode])();
}
}
}
void Script::updateInput() {
_stub->processEvents();
if (_res->_currentPart == kPartPassword) {
const char c = _stub->_pi.lastChar;
if (c == 8 || /*c == 0xD ||*/ c == 0 || (c >= 'a' && c <= 'z')) {
_scriptVars[VAR_LAST_KEYCHAR] = c & ~0x20;
_stub->_pi.lastChar = 0;
}
}
int16 lr = 0;
int16 m = 0;
int16 ud = 0;
int16 jd = 0;
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
lr = 1;
m |= 1;
}
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
lr = -1;
m |= 2;
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
ud = jd = 1;
m |= 4; // crouch
}
if (_is3DO) { // This could be enabled to any later version than Amiga, Atari and DOS demo
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
ud = -1;
}
if (_stub->_pi.jump) {
jd = -1;
m |= 8; // jump
}
} else {
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
ud = jd = -1;
m |= 8; // jump
}
}
if (!(_res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI)) {
_scriptVars[VAR_HERO_POS_UP_DOWN] = ud;
}
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = jd;
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = lr;
_scriptVars[VAR_HERO_POS_MASK] = m;
int16 action = 0;
if (_stub->_pi.action) {
action = 1;
m |= 0x80;
}
_scriptVars[VAR_HERO_ACTION] = action;
_scriptVars[VAR_HERO_ACTION_POS_MASK] = m;
if (_res->_currentPart == kPartWater) {
const uint8 mask = _res->_demo3Joy.update();
if (mask != 0) {
_scriptVars[VAR_HERO_ACTION_POS_MASK] = mask;
_scriptVars[VAR_HERO_POS_MASK] = mask & 15;
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = 0;
if (mask & 1) {
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = 1;
}
if (mask & 2) {
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = -1;
}
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = 0;
if (mask & 4) {
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = 1;
}
if (mask & 8) {
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = -1;
}
_scriptVars[VAR_HERO_ACTION] = (mask >> 7);
}
}
}
void Script::inp_handleSpecialKeys() {
if (_stub->_pi.pause) {
if (_res->_currentPart != kPartCopyProtection && _res->_currentPart != kPartIntro) {
_stub->_pi.pause = false;
if (_is3DO) {
_vid->drawBitmap3DO("PauseShape", _stub);
}
while (!_stub->_pi.pause && !_stub->_pi.quit) {
_stub->processEvents();
_stub->sleep(50);
}
}
_stub->_pi.pause = false;
}
if (_stub->_pi.back) {
_stub->_pi.back = false;
if (_is3DO) {
static const char *names[] = { "EndShape1", "EndShape2" };
int current = 0;
_vid->drawBitmap3DO(names[current], _stub);
while (!_stub->_pi.quit) {
_stub->processEvents();
_stub->sleep(50);
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT;
if (current != 0) {
current = 0;
_vid->drawBitmap3DO(names[current], _stub);
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
if (current != 1) {
current = 1;
_vid->drawBitmap3DO(names[current], _stub);
}
}
if (_stub->_pi.action) {
_stub->_pi.action = false;
if (current == 0) {
_res->_nextPart = 16000;
}
break;
}
}
}
}
if (_stub->_pi.code) {
_stub->_pi.code = false;
if (_res->_hasPasswordScreen) {
if (_res->_currentPart != kPartPassword && _res->_currentPart != kPartCopyProtection) {
_res->_nextPart = kPartPassword;
}
}
}
}
static uint8 getWavLooping(uint16 resNum) {
switch (resNum) {
case 1:
case 3:
case 8:
case 16:
case 89:
case 97:
case 102:
case 104:
case 106:
case 132:
case 139:
return 1;
default:
break;
}
return 0;
}
static int getSoundFreq(uint8 period) {
if (period > 39) {
warning("Script::getSoundFreq() invalid period %d", period);
period = 39;
}
return kPaulaFreq / (Script::PERIOD_TABLE[period] * 2);
}
void Script::snd_playSound(uint16 resNum, uint8 freq, uint8 vol, uint8 channel) {
debugC(kDebugSound, "snd_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
if (vol == 0) {
_sound->stopSound(channel);
return;
}
if (vol > 63) {
vol = 63;
}
if (freq > 39) {
freq = 39;
}
channel &= 3;
switch (_res->getDataType()) {
case DT_20TH_EDITION:
if (freq != 0) {
--freq;
}
// fall-through
case DT_15TH_EDITION:
if (freq >= 32) {
// Anniversary editions do not have the 170 period
//
// [31] dos=19886 20th=19886 amiga=19886 (period 180)
// [32] dos=21056 20th=22372 amiga=21056 (period 170)
// [33] dos=22372 20th=23704 amiga=22372 (period 160)
++freq;
}
// fall-through
case DT_WIN31: {
uint32 size = 0;
uint8 *buf = _res->loadWav(resNum, &size);
if (buf) {
_sound->playSoundWav(channel, buf, size,
getSoundFreq(freq), vol, getWavLooping(resNum));
}
break;
}
case DT_3DO:
_sound->playSoundAiff(channel, resNum, vol);
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS: {
MemEntry *me = &_res->_memList[resNum];
if (me->status == Resource::STATUS_LOADED) {
_sound->playSoundRaw(channel, me->bufPtr, me->unpackedSize, getSoundFreq(freq), vol);
}
break;
}
default:
break;
}
}
void Script::snd_playMusic(uint16 resNum, uint16 delay, uint8 pos) {
debugC(kDebugSound, "snd_playMusic(0x%X, %d, %d)", resNum, delay, pos);
uint8 loop = 0;
switch (_res->getDataType()) {
case DT_20TH_EDITION:
if (resNum == 5000) {
_sound->stopMusic();
break;
}
if (resNum >= 5001 && resNum <= 5010) {
loop = 1;
}
// fall-through
case DT_15TH_EDITION:
case DT_WIN31:
if (resNum != 0) {
char path[MAXPATHLEN];
const char *p = _res->getMusicPath(resNum, path, sizeof(path));
if (p) {
_sound->playMusic(p, loop);
}
}
break;
case DT_3DO:
if (resNum == 0) {
_sound->stopAifcMusic();
} else {
uint32 offset = 0;
char path[MAXPATHLEN];
const char *p = _res->getMusicPath(resNum, path, sizeof(path), &offset);
if (p) {
_sound->playAifcMusic(p, offset);
}
}
break;
default: // DT_AMIGA, DT_ATARI, DT_DOS
if (resNum != 0) {
_ply->loadSfxModule(resNum, delay, pos);
_ply->start();
_sound->playSfxMusic(resNum);
} else if (delay != 0) {
_ply->setEventsDelay(delay);
} else {
_sound->stopSfxMusic();
}
break;
}
}
void Script::snd_preloadSound(uint16 resNum, const uint8 *data) {
if (_res->getDataType() == DT_3DO) {
_sound->preloadSoundAiff(resNum, data);
}
}
void Script::fixUpPalette_changeScreen(int part, int screen) {
int pal = -1;
switch (part) {
case 16004:
if (screen == 0x47) { // bitmap resource #68
pal = 8;
}
break;
case 16006:
if (screen == 0x4A) { // bitmap resources #144, #145
pal = 1;
}
break;
default:
break;
}
if (pal != -1) {
debugC(kDebugScript, "Setting palette %d for part %d screen %d", pal, part, screen);
_vid->changePal(pal);
}
}
} // namespace Awe

137
engines/awe/script.h Normal file
View File

@@ -0,0 +1,137 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_SCRIPT_H
#define AWE_SCRIPT_H
#include "audio/mixer.h"
#include "awe/intern.h"
namespace Awe {
class Sound;
struct Resource;
struct SfxPlayer;
struct SystemStub;
struct Video;
enum Difficulty {
DIFFICULTY_EASY = 0,
DIFFICULTY_NORMAL = 1,
DIFFICULTY_HARD = 2
};
struct Script {
typedef void (Script:: *OpcodeStub)();
enum ScriptVars {
VAR_RANDOM_SEED = 0x3C,
VAR_SCREEN_NUM = 0x67,
VAR_LAST_KEYCHAR = 0xDA,
VAR_HERO_POS_UP_DOWN = 0xE5,
VAR_MUSIC_SYNC = 0xF4,
VAR_SCROLL_Y = 0xF9,
VAR_HERO_ACTION = 0xFA,
VAR_HERO_POS_JUMP_DOWN = 0xFB,
VAR_HERO_POS_LEFT_RIGHT = 0xFC,
VAR_HERO_POS_MASK = 0xFD,
VAR_HERO_ACTION_POS_MASK = 0xFE,
VAR_PAUSE_SLICES = 0xFF
};
static const OpcodeStub OPCODE_TABLE[];
static const uint16 PERIOD_TABLE[];
static Difficulty _difficulty;
static bool _useRemasteredAudio;
Sound *_sound;
Resource *_res;
SfxPlayer *_ply;
Video *_vid;
SystemStub *_stub = nullptr;
int16 _scriptVars[256] = { 0 };
uint16 _scriptStackCalls[64] = { 0 };
uint16 _scriptTasks[2][64] = { { 0 } };
uint8 _scriptStates[2][64] = { { 0 } };
Ptr _scriptPtr;
uint8 _stackPtr = 0;
bool _scriptPaused = false;
bool _fastMode = false;
int _screenNum = 0;
bool _is3DO = false;
uint32 _startTime = 0, _timeStamp = 0;
Script(Sound *snd, Resource *res, SfxPlayer *ply, Video *vid);
void init();
void op_movConst();
void op_mov();
void op_add();
void op_addConst();
void op_call();
void op_ret();
void op_yieldTask();
void op_jmp();
void op_installTask();
void op_jmpIfVar();
void op_condJmp();
void op_setPalette();
void op_changeTasksState();
void op_selectPage();
void op_fillPage();
void op_copyPage();
void op_updateDisplay();
void op_removeTask();
void op_drawString();
void op_sub();
void op_and();
void op_or();
void op_shl();
void op_shr();
void op_playSound();
void op_updateResources();
void op_playMusic();
void restartAt(int part, int pos = -1);
void setupPart(int num);
void setupTasks();
void runTasks();
void executeTask();
void updateInput();
void inp_handleSpecialKeys();
void snd_playSound(uint16 resNum, uint8 freq, uint8 vol, uint8 channel);
void snd_playMusic(uint16 resNum, uint16 delay, uint8 pos);
void snd_preloadSound(uint16 resNum, const uint8 *data);
void fixUpPalette_changeScreen(int part, int screen);
};
} // namespace Awe
#endif

274
engines/awe/sfx_player.cpp Normal file
View File

@@ -0,0 +1,274 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "awe/sfx_player.h"
#include "awe/resource.h"
#include "awe/sound.h"
#include "awe/system_stub.h"
namespace Awe {
void SfxInstrument::clear() {
data = nullptr;
volume = 0;
}
void SfxModule::clear() {
data = nullptr;
curPos = 0;
curOrder = 0;
numOrder = 0;
orderTable = nullptr;
clearSamples();
}
void SfxModule::clearSamples() {
for (int i = 0; i < 15; ++i)
samples[i].clear();
}
SfxPlayer::SfxPlayer(Resource *res)
: _res(res), _delay(0) {
_playing = false;
}
void SfxPlayer::setEventsDelay(uint16 delay) {
debugC(kDebugSound, "SfxPlayer::setEventsDelay(%d)", delay);
_delay = delay;
}
void SfxPlayer::loadSfxModule(uint16 resNum, uint16 delay, uint8 pos) {
debugC(kDebugSound, "SfxPlayer::loadSfxModule(0x%X, %d, %d)", resNum, delay, pos);
MemEntry *me = &_res->_memList[resNum];
if (me->status == Resource::STATUS_LOADED && me->type == Resource::RT_MUSIC) {
_sfxMod.clear();
_sfxMod.curOrder = pos;
_sfxMod.numOrder = me->bufPtr[0x3F];
debugC(kDebugSound, "SfxPlayer::loadSfxModule() curOrder = 0x%X numOrder = 0x%X", _sfxMod.curOrder, _sfxMod.numOrder);
_sfxMod.orderTable = me->bufPtr + 0x40;
if (delay == 0) {
_delay = READ_BE_UINT16(me->bufPtr);
} else {
_delay = delay;
}
_sfxMod.data = me->bufPtr + 0xC0;
debugC(kDebugSound, "SfxPlayer::loadSfxModule() eventDelay = %d ms", _delay);
prepareInstruments(me->bufPtr + 2);
} else {
warning("SfxPlayer::loadSfxModule() ec=0x%X", 0xF8);
}
}
void SfxPlayer::prepareInstruments(const uint8 *p) {
_sfxMod.clearSamples();
for (int i = 0; i < 15; ++i) {
SfxInstrument *ins = &_sfxMod.samples[i];
const uint16 resNum = READ_BE_UINT16(p); p += 2;
if (resNum != 0) {
ins->volume = READ_BE_UINT16(p);
MemEntry *me = &_res->_memList[resNum];
if (me->status == Resource::STATUS_LOADED && me->type == Resource::RT_SOUND) {
ins->data = me->bufPtr;
debugC(kDebugSound, "Loaded instrument 0x%X n=%d volume=%d", resNum, i, ins->volume);
} else {
error("Error loading instrument 0x%X", resNum);
}
}
p += 2; // skip volume
}
}
void SfxPlayer::play(int rate) {
_playing = true;
_rate = rate;
_samplesLeft = 0;
for (int i = 0; i < NUM_CHANNELS; i++) {
_channels[i].sampleData = nullptr;
_channels[i].sampleLen = 0;
_channels[i].sampleLoopPos = 0;
_channels[i].sampleLoopLen = 0;
_channels[i].volume = 0;
_channels[i].pos.inc = 0;
_channels[i].pos.offset = 0;
}
}
static int16 toS16(int a) {
if (a <= -128) {
return -32768;
} else if (a >= 127) {
return 32767;
} else {
const uint8 u8 = (a ^ 0x80);
return ((u8 << 8) | u8) - 32768;
}
}
static void mixChannel(int16 &s, SfxChannel *ch) {
if (ch->sampleLen == 0) {
return;
}
const int pos1 = ch->pos.offset >> Frac::BITS;
ch->pos.offset += ch->pos.inc;
int pos2 = pos1 + 1;
if (ch->sampleLoopLen != 0) {
if (pos1 >= ch->sampleLoopPos + ch->sampleLoopLen - 1) {
pos2 = ch->sampleLoopPos;
ch->pos.offset = pos2 << Frac::BITS;
}
} else {
if (pos1 >= ch->sampleLen - 1) {
ch->sampleLen = 0;
return;
}
}
int sample = ch->pos.interpolate((int8)ch->sampleData[pos1], (int8)ch->sampleData[pos2]);
sample = s + toS16(sample * ch->volume / 64);
s = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample));
}
void SfxPlayer::mixSamples(int16 *buf, int len) {
while (len != 0) {
if (_samplesLeft == 0) {
handleEvents();
const int samplesPerTick = _rate * (_delay * 60 * 1000 / kPaulaFreq) / 1000;
_samplesLeft = samplesPerTick;
}
int count = _samplesLeft;
if (count > len) {
count = len;
}
_samplesLeft -= count;
len -= count;
for (int i = 0; i < count; ++i) {
mixChannel(*buf, &_channels[0]);
mixChannel(*buf, &_channels[3]);
++buf;
mixChannel(*buf, &_channels[1]);
mixChannel(*buf, &_channels[2]);
++buf;
}
}
}
void SfxPlayer::readSamples(int16 *buf, int len) {
if (_delay != 0) {
mixSamples(buf, len / 2);
}
}
void SfxPlayer::start() {
debugC(kDebugSound, "SfxPlayer::start()");
_sfxMod.curPos = 0;
}
void SfxPlayer::stop() {
debugC(kDebugSound, "SfxPlayer::stop()");
_playing = false;
}
void SfxPlayer::handleEvents() {
uint8 order = _sfxMod.orderTable[_sfxMod.curOrder];
const uint8 *patternData = _sfxMod.data + _sfxMod.curPos + order * 1024;
for (uint8 ch = 0; ch < 4; ++ch) {
handlePattern(ch, patternData);
patternData += 4;
}
_sfxMod.curPos += 4 * 4;
debugC(kDebugSound, "SfxPlayer::handleEvents() order = 0x%X curPos = 0x%X", order, _sfxMod.curPos);
if (_sfxMod.curPos >= 1024) {
_sfxMod.curPos = 0;
order = _sfxMod.curOrder + 1;
if (order == _sfxMod.numOrder) {
_playing = false;
}
_sfxMod.curOrder = order;
}
}
void SfxPlayer::handlePattern(uint8 channel, const uint8 *data) {
SfxPattern pat;
pat.note_1 = READ_BE_UINT16(data + 0);
pat.note_2 = READ_BE_UINT16(data + 2);
if (pat.note_1 != 0xFFFD) {
const uint16 sample = (pat.note_2 & 0xF000) >> 12;
if (sample != 0) {
uint8 *ptr = _sfxMod.samples[sample - 1].data;
if (ptr != nullptr) {
debugC(kDebugSound, "SfxPlayer::handlePattern() preparing sample %d", sample);
pat.sampleVolume = _sfxMod.samples[sample - 1].volume;
pat.sampleStart = 8;
pat.sampleBuffer = ptr;
pat.sampleLen = READ_BE_UINT16(ptr) * 2;
const uint16 loopLen = READ_BE_UINT16(ptr + 2) * 2;
if (loopLen != 0) {
pat.loopPos = pat.sampleLen;
pat.loopLen = loopLen;
} else {
pat.loopPos = 0;
pat.loopLen = 0;
}
int16 m = pat.sampleVolume;
const uint8 effect = (pat.note_2 & 0x0F00) >> 8;
if (effect == 5) { // volume up
const uint8 volume = (pat.note_2 & 0xFF);
m += volume;
if (m > 0x3F) {
m = 0x3F;
}
} else if (effect == 6) { // volume down
const uint8 volume = (pat.note_2 & 0xFF);
m -= volume;
if (m < 0) {
m = 0;
}
}
_channels[channel].volume = m;
pat.sampleVolume = m;
}
}
}
if (pat.note_1 == 0xFFFD) {
debugC(kDebugSound, "SfxPlayer::handlePattern() _syncVar = 0x%X", pat.note_2);
*_syncVar = pat.note_2;
} else if (pat.note_1 == 0xFFFE) {
_channels[channel].sampleLen = 0;
} else if (pat.note_1 != 0 && pat.sampleBuffer != nullptr) {
assert(pat.note_1 >= 0x37 && pat.note_1 < 0x1000);
// convert Amiga period value to hz
const int freq = kPaulaFreq / (pat.note_1 * 2);
debugC(kDebugSound, "SfxPlayer::handlePattern() adding sample freq = 0x%X", freq);
SfxChannel *ch = &_channels[channel];
ch->sampleData = pat.sampleBuffer + pat.sampleStart;
ch->sampleLen = pat.sampleLen;
ch->sampleLoopPos = pat.loopPos;
ch->sampleLoopLen = pat.loopLen;
ch->volume = pat.sampleVolume;
ch->pos.offset = 0;
ch->pos.inc = (freq << Frac::BITS) / _rate;
}
}
} // namespace Awe

102
engines/awe/sfx_player.h Normal file
View File

@@ -0,0 +1,102 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_SFX_PLAYER_H
#define AWE_SFX_PLAYER_H
#include "awe/intern.h"
namespace Awe {
struct SfxInstrument {
uint8 *data = nullptr;
uint16 volume = 0;
void clear();
};
struct SfxModule {
const uint8 *data = nullptr;
uint16 curPos = 0;
uint8 curOrder = 0;
uint8 numOrder = 0;
uint8 *orderTable = nullptr;
SfxInstrument samples[15];
void clear();
void clearSamples();
};
struct SfxPattern {
uint16 note_1 = 0;
uint16 note_2 = 0;
uint16 sampleStart = 0;
uint8 *sampleBuffer = nullptr;
uint16 sampleLen = 0;
uint16 loopPos = 0;
uint16 loopLen = 0;
uint16 sampleVolume = 0;
};
struct SfxChannel {
uint8 *sampleData = nullptr;
uint16 sampleLen = 0;
uint16 sampleLoopPos = 0;
uint16 sampleLoopLen = 0;
uint16 volume = 0;
Frac pos;
};
struct Resource;
struct SfxPlayer {
enum {
NUM_CHANNELS = 4
};
Resource *_res;
uint16 _delay = 0;
uint16 _resNum = 0;
SfxModule _sfxMod;
int16 *_syncVar = nullptr;
bool _playing = false;
int _rate = 0;
int _samplesLeft = 0;
SfxChannel _channels[NUM_CHANNELS];
SfxPlayer(Resource *res);
void setEventsDelay(uint16 delay);
void loadSfxModule(uint16 resNum, uint16 delay, uint8 pos);
void prepareInstruments(const uint8 *p);
void play(int rate);
void mixSamples(int16 *buf, int len);
void readSamples(int16 *buf, int len);
void start();
void stop();
void handleEvents();
void handlePattern(uint8 channel, const uint8 *patternData);
};
} // namespace Awe
#endif

630
engines/awe/sound.cpp Normal file
View File

@@ -0,0 +1,630 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
//define MIX_INIT_FLUIDSYNTH MIX_INIT_MID // renamed with SDL2_mixer >= 2.0.2
#include "audio/mixer.h"
#include "audio/audiostream.h"
#include "audio/mididrv.h"
#include "audio/midiparser.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/wave.h"
#include "common/memstream.h"
#include "awe/aifc_player.h"
#include "awe/sound.h"
#include "awe/sfx_player.h"
namespace Awe {
void Sound::playMusic(const char *path, int loops) {
warning("TODO: playMusic");
}
void Sound::playSfxMusic(int num) {
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, _sfxStream, -1, 255, 0, DisposeAfterUse::YES, true);
_sfx->play(_mixer->getOutputRate());
}
void Sound::playAifcMusic(const char *path, uint32 offset) {
warning("TODO: playAifcMusic");
}
void Sound::stopMusic() {
warning("TODO: stopMusic");
}
void Sound::stopAifcMusic() {
warning("TODO: stopAifcMusic");
}
void Sound::stopAll() {
stopSfxMusic();
_mixer->stopAll();
}
void Sound::setPlayer(SfxPlayer *player) {
_sfx = player;
_sfxStream = new SfxMusicStream(player);
}
void Sound::stopSfxMusic() {
if (_mixer->isSoundHandleActive(_musicHandle)) {
_mixer->stopHandle(_musicHandle);
}
}
void Sound::preloadSoundAiff(byte num, const byte *data) {
warning("TODO: preloadSoundAiff");
}
void Sound::playSoundRaw(byte channel, const byte *data, size_t size,
int freq, byte volume) {
assert(channel < MAX_CHANNELS);
// Used for looping sounds, e.g. the waterfall
// uint16 length = READ_BE_UINT16(data) * 2;
// uint16 loopLength = READ_BE_UINT16(data + 2) * 2;
Common::MemoryReadStream *stream =
new Common::MemoryReadStream(data + 8, size - 8);
Audio::AudioStream *sound =
Audio::makeRawStream(stream, freq,
0,
DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[channel],
sound, -1, 255, 0, DisposeAfterUse::YES);
}
void Sound::playSoundWav(byte channel, const byte *data, size_t size,
uint16 freq, byte volume, byte loop) {
assert(channel < MAX_CHANNELS);
Common::MemoryReadStream *stream =
new Common::MemoryReadStream(data, size);
Audio::AudioStream *sound = Audio::makeWAVStream(
stream, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[channel],
sound, -1, 255, 0, DisposeAfterUse::YES);
}
void Sound::playSoundAiff(byte channel, byte num, byte volume) {
warning("TODO: playSoundAiff");
}
void Sound::stopSound(byte channel) {
assert(channel < MAX_CHANNELS);
_mixer->stopHandle(_channels[channel]);
}
#ifdef TODO
enum {
TAG_RIFF = 0x46464952,
TAG_WAVE = 0x45564157,
TAG_fmt = 0x20746D66,
TAG_data = 0x61746164
};
static const bool kAmigaStereoChannels = false; // 0,3:left 1,2:right
static int16 toS16(int a) {
return ((a << 8) | a) - 32768;
}
static int16 mixS16(int sample1, int sample2) {
const int sample = sample1 + sample2;
return sample < -32768 ? -32768 : ((sample > 32767 ? 32767 : sample));
}
struct MixerChannel {
const byte *_data;
Frac _pos;
uint32 _len;
uint32 _loopLen, _loopPos;
int _volume;
void (MixerChannel:: *_mixWav)(int16 *sample, int count);
void initRaw(const byte *data, int freq, int volume, int mixingFreq) {
_data = data + 8;
_pos.reset(freq, mixingFreq);
const int len = READ_BE_UINT16(data) * 2;
_loopLen = READ_BE_UINT16(data + 2) * 2;
_loopPos = _loopLen ? len : 0;
_len = len;
_volume = volume;
}
void initWav(const byte *data, int freq, int volume, int mixingFreq, int len, bool bits16, bool stereo, bool loop) {
_data = data;
_pos.reset(freq, mixingFreq);
_len = len;
_loopLen = loop ? len : 0;
_loopPos = 0;
_volume = volume;
_mixWav = bits16 ? (stereo ? &MixerChannel::mixWav<16, true> : &MixerChannel::mixWav<16, false>) : (stereo ? &MixerChannel::mixWav<8, true> : &MixerChannel::mixWav<8, false>);
}
void mixRaw(int16 &sample) {
if (_data) {
uint32 pos = _pos.getInt();
_pos.offset += _pos.inc;
if (_loopLen != 0) {
if (pos >= _loopPos + _loopLen) {
pos = _loopPos;
_pos.offset = (_loopPos << Frac::BITS) + _pos.inc;
}
} else {
if (pos >= _len) {
_data = 0;
return;
}
}
sample = mixS16(sample, toS16(_data[pos] ^ 0x80) * _volume / 64);
}
}
template<int bits, bool stereo>
void mixWav(int16 *samples, int count) {
for (int i = 0; i < count; i += 2) {
uint32 pos = _pos.getInt();
_pos.offset += _pos.inc;
if (pos >= _len) {
if (_loopLen != 0) {
pos = 0;
_pos.offset = _pos.inc;
} else {
_data = 0;
break;
}
}
if (stereo) {
pos *= 2;
}
int valueL;
if (bits == 8) { // U8
valueL = toS16(_data[pos]) * _volume / 64;
} else { // S16
valueL = ((int16)READ_LE_UINT16(&_data[pos * sizeof(int16)])) * _volume / 64;
}
*samples = mixS16(*samples, valueL);
++samples;
int valueR;
if (!stereo) {
valueR = valueL;
} else {
if (bits == 8) { // U8
valueR = toS16(_data[pos + 1]) * _volume / 64;
} else { // S16
valueR = ((int16)READ_LE_UINT16(&_data[(pos + 1) * sizeof(int16)])) * _volume / 64;
}
}
*samples = mixS16(*samples, valueR);
++samples;
}
}
};
static const byte *loadWav(const byte *data, int &freq, int &len, bool &bits16, bool &stereo) {
uint32 riffMagic = READ_LE_UINT32(data);
if (riffMagic != TAG_RIFF) return 0;
uint32 riffLength = READ_LE_UINT32(data + 4);
uint32 waveMagic = READ_LE_UINT32(data + 8);
if (waveMagic != TAG_WAVE) return 0;
uint32 offset = 12;
uint32 chunkMagic, chunkLength = 0;
// find fmt chunk
do {
offset += chunkLength + (chunkLength & 1);
if (offset >= riffLength) return 0;
chunkMagic = READ_LE_UINT32(data + offset);
chunkLength = READ_LE_UINT32(data + offset + 4);
offset += 8;
} while (chunkMagic != TAG_fmt);
if (chunkLength < 14) return 0;
if (offset + chunkLength >= riffLength) return 0;
// read format
int formatTag = READ_LE_UINT16(data + offset);
int channels = READ_LE_UINT16(data + offset + 2);
int samplesPerSec = READ_LE_UINT32(data + offset + 4);
int bitsPerSample = 0;
if (chunkLength >= 16) {
bitsPerSample = READ_LE_UINT16(data + offset + 14);
} else if (formatTag == 1 && channels != 0) {
int blockAlign = READ_LE_UINT16(data + offset + 12);
bitsPerSample = (blockAlign * 8) / channels;
}
// check supported format
if ((formatTag != 1) || // PCM
(channels != 1 && channels != 2) || // mono or stereo
(bitsPerSample != 8 && bitsPerSample != 16)) { // 8bit or 16bit
warning("Unsupported wave file");
return 0;
}
// find data chunk
do {
offset += chunkLength + (chunkLength & 1);
if (offset >= riffLength) return 0;
chunkMagic = READ_LE_UINT32(data + offset);
chunkLength = READ_LE_UINT32(data + offset + 4);
offset += 8;
} while (chunkMagic != TAG_data);
uint32 lengthSamples = chunkLength;
if (offset + lengthSamples - 4 > riffLength) {
lengthSamples = riffLength + 4 - offset;
}
if (channels == 2) lengthSamples >>= 1;
if (bitsPerSample == 16) lengthSamples >>= 1;
freq = samplesPerSec;
len = lengthSamples;
bits16 = (bitsPerSample == 16);
stereo = (channels == 2);
return data + offset;
}
struct Mixer_impl {
static const int kMixFreq = 44100;
static const SDL_AudioFormat kMixFormat = AUDIO_S16SYS;
static const int kMixSoundChannels = 2;
static const int kMixBufSize = 4096;
static const int kMixChannels = 4;
Mix_Chunk *_sounds[kMixChannels];
Mix_Music *_music;
MixerChannel _channels[kMixChannels];
SfxPlayer *_sfx;
std::map<int, Mix_Chunk *> _preloads; // AIFF preloads (3DO)
void init(MixerType mixerType) {
memset(_sounds, 0, sizeof(_sounds));
_music = 0;
memset(_channels, 0, sizeof(_channels));
for (int i = 0; i < kMixChannels; ++i) {
_channels[i]._mixWav = &MixerChannel::mixWav<8, false>;
}
_sfx = 0;
Mix_Init(MIX_INIT_OGG | MIX_INIT_FLUIDSYNTH);
if (Mix_OpenAudio(kMixFreq, kMixFormat, kMixSoundChannels, kMixBufSize) < 0) {
warning("Mix_OpenAudio failed: %s", Mix_GetError());
}
switch (mixerType) {
case kMixerTypeRaw:
Mix_HookMusic(mixAudio, this);
break;
case kMixerTypeWav:
Mix_SetPostMix(mixAudioWav, this);
break;
case kMixerTypeAiff:
Mix_AllocateChannels(kMixChannels);
break;
default:
break;
}
}
void quit() {
stopAll();
Mix_CloseAudio();
Mix_Quit();
}
void update() {
for (int i = 0; i < kMixChannels; ++i) {
if (_sounds[i] && !Mix_Playing(i)) {
freeSound(i);
}
}
}
void playSoundRaw(byte channel, const byte *data, int freq, byte volume) {
SDL_LockAudio();
_channels[channel].initRaw(data, freq, volume, kMixFreq);
SDL_UnlockAudio();
}
void playSoundWav(byte channel, const byte *data, int freq, byte volume, bool loop) {
int wavFreq, len;
bool bits16, stereo;
const byte *wavData = loadWav(data, wavFreq, len, bits16, stereo);
if (!wavData) return;
if (wavFreq == 22050 || wavFreq == 44100 || wavFreq == 48000) {
freq = (int)(freq * (wavFreq / 9943.0f));
}
SDL_LockAudio();
_channels[channel].initWav(wavData, freq, volume, kMixFreq, len, bits16, stereo, loop);
SDL_UnlockAudio();
}
void playSound(byte channel, int volume, Mix_Chunk *chunk, int loops = 0) {
stopSound(channel);
if (chunk) {
Mix_PlayChannel(channel, chunk, loops);
}
setChannelVolume(channel, volume);
_sounds[channel] = chunk;
}
void stopSound(byte channel) {
SDL_LockAudio();
_channels[channel]._data = 0;
SDL_UnlockAudio();
Mix_HaltChannel(channel);
freeSound(channel);
}
void freeSound(int channel) {
Mix_FreeChunk(_sounds[channel]);
_sounds[channel] = 0;
}
void setChannelVolume(byte channel, byte volume) {
SDL_LockAudio();
_channels[channel]._volume = volume;
SDL_UnlockAudio();
Mix_Volume(channel, volume * MIX_MAX_VOLUME / 63);
}
void playMusic(const char *path, int loops = 0) {
stopMusic();
_music = Mix_LoadMUS(path);
if (_music) {
Mix_VolumeMusic(MIX_MAX_VOLUME / 2);
Mix_PlayMusic(_music, loops);
} else {
warning("Failed to load music '%s', %s", path, Mix_GetError());
}
}
void stopMusic() {
Mix_HaltMusic();
Mix_FreeMusic(_music);
_music = 0;
}
static void mixAifcPlayer(void *data, byte *s16buf, int len) {
((AifcPlayer *)data)->readSamples((int16 *)s16buf, len / 2);
}
void playAifcMusic(AifcPlayer *aifc) {
Mix_HookMusic(mixAifcPlayer, aifc);
}
void stopAifcMusic() {
Mix_HookMusic(0, 0);
}
void playSfxMusic(SfxPlayer *sfx) {
SDL_LockAudio();
_sfx = sfx;
_sfx->play(kMixFreq);
SDL_UnlockAudio();
}
void stopSfxMusic() {
SDL_LockAudio();
if (_sfx) {
_sfx->stop();
_sfx = 0;
}
SDL_UnlockAudio();
}
void mixChannels(int16 *samples, int count) {
if (kAmigaStereoChannels) {
for (int i = 0; i < count; i += 2) {
_channels[0].mixRaw(*samples);
_channels[3].mixRaw(*samples);
++samples;
_channels[1].mixRaw(*samples);
_channels[2].mixRaw(*samples);
++samples;
}
} else {
for (int i = 0; i < count; i += 2) {
for (int j = 0; j < kMixChannels; ++j) {
_channels[j].mixRaw(samples[i]);
}
samples[i + 1] = samples[i];
}
}
}
static void mixAudio(void *data, byte *s16buf, int len) {
memset(s16buf, 0, len);
Mixer_impl *mixer = (Mixer_impl *)data;
mixer->mixChannels((int16 *)s16buf, len / sizeof(int16));
if (mixer->_sfx) {
mixer->_sfx->readSamples((int16 *)s16buf, len / sizeof(int16));
}
}
void mixChannelsWav(int16 *samples, int count) {
for (int i = 0; i < kMixChannels; ++i) {
if (_channels[i]._data) {
(_channels[i].*_channels[i]._mixWav)(samples, count);
}
}
}
static void mixAudioWav(void *data, byte *s16buf, int len) {
Mixer_impl *mixer = (Mixer_impl *)data;
mixer->mixChannelsWav((int16 *)s16buf, len / sizeof(int16));
}
void stopAll() {
for (int i = 0; i < kMixChannels; ++i) {
stopSound(i);
}
stopMusic();
stopSfxMusic();
for (std::map<int, Mix_Chunk *>::iterator it = _preloads.begin(); it != _preloads.end(); ++it) {
debugC(kDebugSound, "Flush preload %d", it->first);
Mix_FreeChunk(it->second);
}
_preloads.clear();
}
void preloadSoundAiff(int num, const byte *data) {
if (_preloads.find(num) != _preloads.end()) {
warning("AIFF sound %d is already preloaded", num);
} else {
const uint32 size = READ_BE_UINT32(data + 4) + 8;
SDL_RWops *rw = SDL_RWFromConstMem(data, size);
Mix_Chunk *chunk = Mix_LoadWAV_RW(rw, 1);
_preloads[num] = chunk;
}
}
void playSoundAiff(int channel, int num, int volume) {
if (_preloads.find(num) == _preloads.end()) {
warning("AIFF sound %d is not preloaded", num);
} else {
Mix_Chunk *chunk = _preloads[num];
Mix_PlayChannel(channel, chunk, 0);
Mix_Volume(channel, volume * MIX_MAX_VOLUME / 63);
}
}
};
Mixer::Mixer(SfxPlayer *sfx)
: _aifc(0), _sfx(sfx) {
}
void Mixer::init(MixerType mixerType) {
_impl = new Mixer_impl();
_impl->init(mixerType);
}
void Mixer::quit() {
stopAll();
if (_impl) {
_impl->quit();
delete _impl;
}
delete _aifc;
}
void Mixer::update() {
if (_impl) {
_impl->update();
}
}
void Mixer::playSoundRaw(byte channel, const byte *data, uint16 freq, byte volume) {
debugC(kDebugSound, "Mixer::playChannel(%d, %d, %d)", channel, freq, volume);
if (_impl) {
return _impl->playSoundRaw(channel, data, freq, volume);
}
}
void Mixer::playSoundWav(byte channel, const byte *data, uint16 freq, byte volume, byte loop) {
debugC(kDebugSound, "Mixer::playSoundWav(%d, %d, %d)", channel, volume, loop);
if (_impl) {
return _impl->playSoundWav(channel, data, freq, volume, loop);
}
}
void Mixer::stopSound(byte channel) {
debugC(kDebugSound, "Mixer::stopChannel(%d)", channel);
if (_impl) {
return _impl->stopSound(channel);
}
}
void Mixer::setChannelVolume(byte channel, byte volume) {
debugC(kDebugSound, "Mixer::setChannelVolume(%d, %d)", channel, volume);
if (_impl) {
return _impl->setChannelVolume(channel, volume);
}
}
void Mixer::playMusic(const char *path, byte loop) {
debugC(kDebugSound, "Mixer::playMusic(%s, %d)", path, loop);
if (_impl) {
return _impl->playMusic(path, (loop != 0) ? -1 : 0);
}
}
void Mixer::stopMusic() {
debugC(kDebugSound, "Mixer::stopMusic()");
if (_impl) {
return _impl->stopMusic();
}
}
void Mixer::playAifcMusic(const char *path, uint32 offset) {
debugC(kDebugSound, "Mixer::playAifcMusic(%s)", path);
if (!_aifc) {
_aifc = new AifcPlayer();
}
if (_impl) {
_impl->stopAifcMusic();
if (_aifc->play(Mixer_impl::kMixFreq, path, offset)) {
_impl->playAifcMusic(_aifc);
}
}
}
void Mixer::stopAifcMusic() {
debugC(kDebugSound, "Mixer::stopAifcMusic()");
if (_impl && _aifc) {
_aifc->stop();
_impl->stopAifcMusic();
}
}
void Mixer::playSfxMusic(int num) {
debugC(kDebugSound, "Mixer::playSfxMusic(%d)", num);
if (_impl && _sfx) {
return _impl->playSfxMusic(_sfx);
}
}
void Mixer::stopSfxMusic() {
debugC(kDebugSound, "Mixer::stopSfxMusic()");
if (_impl && _sfx) {
return _impl->stopSfxMusic();
}
}
void Mixer::stopAll() {
debugC(kDebugSound, "Mixer::stopAll()");
if (_impl) {
return _impl->stopAll();
}
}
void Mixer::preloadSoundAiff(byte num, const byte *data) {
debugC(kDebugSound, "Mixer::preloadSoundAiff(num:%d, data:%p)", num, data);
if (_impl) {
return _impl->preloadSoundAiff(num, data);
}
}
void Mixer::playSoundAiff(byte channel, byte num, byte volume) {
debugC(kDebugSound, "Mixer::playSoundAiff()");
if (_impl) {
return _impl->playSoundAiff(channel, num, volume);
}
}
#endif
} // namespace Awe

122
engines/awe/sound.h Normal file
View File

@@ -0,0 +1,122 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_SOUND_H
#define AWE_SOUND_H
#include "audio/mixer.h"
#include "audio/audiostream.h"
#include "awe/intern.h"
#include "awe/sfx_player.h"
namespace Awe {
#define MAX_CHANNELS 8
class SfxMusicStream : public Audio::AudioStream {
private:
SfxPlayer *_player;
public:
explicit SfxMusicStream(SfxPlayer *player) : _player(player) {}
bool isStereo() const override {
return true;
}
virtual int getRate() const override { return _player->_rate; }
virtual bool endOfData() const override { return false; }
int readBuffer(int16 *buffer, const int numSamples) override {
assert(_player != nullptr);
memset(buffer, 0, numSamples * sizeof(int16));
_player->readSamples(buffer, numSamples);
return numSamples;
}
};
class Sound {
private:
Audio::Mixer *_mixer;
Audio::SoundHandle _musicHandle;
SfxPlayer *_sfx = nullptr;
SfxMusicStream *_sfxStream = nullptr;
Audio::SoundHandle _channels[MAX_CHANNELS];
public:
explicit Sound(Audio::Mixer *mixer) : _mixer(mixer) {
}
void setPlayer(SfxPlayer *player);
void stopAll();
void playMusic(const char *path, int loops);
void playAifcMusic(const char *path, uint32 offset);
void playSfxMusic(int num);
void stopMusic();
void stopAifcMusic();
void stopSfxMusic();
void preloadSoundAiff(byte num, const byte *data);
void playSoundRaw(byte channel, const byte *data, size_t size,
int freq, byte volume);
void playSoundWav(byte channel, const byte *data, size_t size,
uint16 freq, byte volume, byte loop);
void playSoundAiff(byte channel, byte num, byte volume);
void stopSound(byte channel);
};
#ifdef TODO
struct AifcPlayer;
struct SfxPlayer;
struct Mixer_impl;
struct Mixer {
AifcPlayer *_aifc;
SfxPlayer *_sfx;
Mixer_impl *_impl;
Mixer(SfxPlayer *sfx);
void init(MixerType mixerType);
void quit();
void update();
void playSoundRaw(byte channel, const byte *data, uint16 freq, byte volume);
void playSoundWav(byte channel, const byte *data, uint16 freq, byte volume, byte loop);
void stopSound(byte channel);
void setChannelVolume(byte channel, byte volume);
void playMusic(const char *path, byte loop);
void stopMusic();
void playAifcMusic(const char *path, uint32 offset);
void stopAifcMusic();
void playSfxMusic(int num);
void stopSfxMusic();
void stopAll();
void preloadSoundAiff(byte num, const byte *data);
void playSoundAiff(byte channel, byte num, byte volume);
};
#endif
} // namespace Awe
#endif

2522
engines/awe/static_res.cpp Normal file

File diff suppressed because it is too large Load Diff

264
engines/awe/system_stub.cpp Normal file
View File

@@ -0,0 +1,264 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "common/events.h"
#include "engines/util.h"
#include "graphics/paletteman.h"
#include "graphics/screen.h"
#include "awe/gfx.h"
#include "awe/metaengine.h"
#include "awe/system_stub.h"
namespace Awe {
struct SystemStubScummVM : SystemStub {
private:
bool _isAnniversaryEdition;
public:
static const int kJoystickIndex = 0;
static const int kJoystickCommitValue = 16384;
static const float kAspectRatio;
::Graphics::Screen *_screen = nullptr;
float _aspectRatio[4] = { 0.0 };
int _screenshot = 0;
SystemStubScummVM(bool isAnniversaryEdition) :
_isAnniversaryEdition(isAnniversaryEdition) {}
~SystemStubScummVM() override;
void init(const DisplayMode &dm) override;
void fini() override;
void prepareScreen(int &w, int &h, float ar[4]) override;
void updateScreen() override;
void setScreenPixels(const Graphics::Surface &src) override;
void setPalette(const Color pal[16]) override;
void processEvents() override;
void sleep(uint32 duration) override;
virtual uint32 getTimeStamp() override;
void setAspectRatio(int w, int h);
};
const float SystemStubScummVM::kAspectRatio = 16.f / 10.f;
SystemStubScummVM::~SystemStubScummVM() {
delete _screen;
}
void SystemStubScummVM::init(const DisplayMode &dm) {
// Initialize backend
initGraphics(dm.width, dm.height, &Gfx::_format);
_screen = new Graphics::Screen();
_screenshot = 1;
_dm = dm;
}
void SystemStubScummVM::fini() {
delete _screen;
_screen = nullptr;
}
void SystemStubScummVM::prepareScreen(int &w, int &h, float ar[4]) {
w = _screen->w;
h = _screen->h;
ar[0] = _aspectRatio[0];
ar[1] = _aspectRatio[1];
ar[2] = _aspectRatio[2];
ar[3] = _aspectRatio[3];
}
void SystemStubScummVM::updateScreen() {
_screen->update();
}
void SystemStubScummVM::setScreenPixels(
const Graphics::Surface &src) {
assert(src.w == _screen->w && src.h == _screen->h);
_screen->blitFrom(src);
}
void SystemStubScummVM::setPalette(const Color pal[16]) {
for (int i = 0; i < 16; ++i)
g_system->getPaletteManager()->setPalette(pal[i].rgb, i, 1);
}
void SystemStubScummVM::processEvents() {
Common::Event ev;
while (g_system->getEventManager()->pollEvent(ev)) {
// WORKAROUND: Anniversary editions have a separate
// jump button than the up arrow; for earlier releases,
// remap the jump to up, which does the same thing
if (!_isAnniversaryEdition &&
ev.customType == KEYBIND_JUMP)
ev.customType = KEYBIND_UP;
switch (ev.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
_pi.quit = true;
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
switch (ev.customType) {
case KEYBIND_LEFT:
_pi.dirMask |= PlayerInput::DIR_LEFT;
break;
case KEYBIND_RIGHT:
_pi.dirMask |= PlayerInput::DIR_RIGHT;
break;
case KEYBIND_UP:
_pi.dirMask |= PlayerInput::DIR_UP;
break;
case KEYBIND_DOWN:
_pi.dirMask |= PlayerInput::DIR_DOWN;
break;
case KEYBIND_SELECT:
_pi.action = true;
break;
case KEYBIND_JUMP:
_pi.jump = true;
break;
case KEYBIND_CODE:
_pi.code = true;
break;
default:
break;
}
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
switch (ev.customType) {
case KEYBIND_LEFT:
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
break;
case KEYBIND_RIGHT:
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
break;
case KEYBIND_UP:
_pi.dirMask &= ~PlayerInput::DIR_UP;
break;
case KEYBIND_DOWN:
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
break;
case KEYBIND_SELECT:
_pi.action = false;
break;
case KEYBIND_JUMP:
_pi.jump = false;
break;
case KEYBIND_CODE:
_pi.code = false;
break;
default:
break;
}
break;
case Common::EVENT_KEYUP:
switch (ev.kbd.keycode) {
case Common::KEYCODE_p:
_pi.pause = true;
break;
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_AC_BACK:
_pi.back = true;
break;
case Common::KEYCODE_AC_HOME:
_pi.quit = true;
break;
default:
break;
}
break;
case Common::EVENT_KEYDOWN:
if (ev.kbd.flags & Common::KBD_ALT) {
if (ev.kbd.keycode == Common::KEYCODE_x) {
_pi.quit = true;
}
break;
} else if (ev.kbd.flags & Common::KBD_CTRL) {
if (ev.kbd.keycode == Common::KEYCODE_f) {
_pi.fastMode = true;
}
break;
}
_pi.lastChar = ev.kbd.keycode;
break;
default:
break;
}
}
}
void SystemStubScummVM::sleep(uint32 duration) {
g_system->delayMillis(duration);
}
uint32 SystemStubScummVM::getTimeStamp() {
return g_system->getMillis();
}
void SystemStubScummVM::setAspectRatio(int w, int h) {
const float currentAspectRatio = w / (float)h;
if (int(currentAspectRatio * 100) == int(kAspectRatio * 100)) {
_aspectRatio[0] = 0.f;
_aspectRatio[1] = 0.f;
_aspectRatio[2] = 1.f;
_aspectRatio[3] = 1.f;
return;
}
// pillar box
if (currentAspectRatio > kAspectRatio) {
const float inset = 1.f - kAspectRatio / currentAspectRatio;
_aspectRatio[0] = inset / 2;
_aspectRatio[1] = 0.f;
_aspectRatio[2] = 1.f - inset;
_aspectRatio[3] = 1.f;
return;
}
// letter box
if (currentAspectRatio < kAspectRatio) {
const float inset = 1.f - currentAspectRatio / kAspectRatio;
_aspectRatio[0] = 0.f;
_aspectRatio[1] = inset / 2;
_aspectRatio[2] = 1.f;
_aspectRatio[3] = 1.f - inset;
return;
}
}
SystemStub *SystemStub_create(bool isAnniversaryEdition) {
return new SystemStubScummVM(isAnniversaryEdition);
}
} // namespace Awe

86
engines/awe/system_stub.h Normal file
View File

@@ -0,0 +1,86 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_SYSTEM_STUB_H
#define AWE_SYSTEM_STUB_H
#include "awe/intern.h"
namespace Awe {
struct PlayerInput {
enum {
DIR_LEFT = 1 << 0,
DIR_RIGHT = 1 << 1,
DIR_UP = 1 << 2,
DIR_DOWN = 1 << 3
};
uint8 dirMask = 0;
bool action = false; // run,shoot
bool jump = false;
bool code = false;
bool pause = false;
bool quit = false;
bool back = false;
char lastChar = '\0';
bool fastMode = false;
};
struct DisplayMode {
enum {
WINDOWED,
FULLSCREEN, // stretch
FULLSCREEN_AR, // 16:10 aspect ratio
} mode;
int width = 0, height = 0; // window dimensions
bool opengl = false; // GL renderer
};
struct SystemStub {
typedef void (*AudioCallback)(void *param, uint8 *stream, int len);
PlayerInput _pi;
DisplayMode _dm;
SystemStub() {}
virtual ~SystemStub() {}
virtual void init(const DisplayMode &dm) = 0;
virtual void fini() = 0;
// GL rendering
virtual void prepareScreen(int &w, int &h, float ar[4]) = 0;
virtual void updateScreen() = 0;
// framebuffer rendering
virtual void setPalette(const Color pal[16]) = 0;
virtual void setScreenPixels(const Graphics::Surface &src) = 0;
virtual void processEvents() = 0;
virtual void sleep(uint32 duration) = 0;
virtual uint32 getTimeStamp() = 0;
};
extern SystemStub *SystemStub_create(bool isAnniversaryEdition);
} // namespace Awe
#endif

125
engines/awe/unpack.cpp Normal file
View File

@@ -0,0 +1,125 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "awe/unpack.h"
namespace Awe {
struct UnpackCtx {
int size;
uint32 crc;
uint32 bits;
uint8 *dst;
const uint8 *src;
};
static bool nextBit(UnpackCtx *uc) {
bool carry = (uc->bits & 1) != 0;
uc->bits >>= 1;
if (uc->bits == 0) { // getnextlwd
uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4;
uc->crc ^= uc->bits;
carry = (uc->bits & 1) != 0;
uc->bits = (1 << 31) | (uc->bits >> 1);
}
return carry;
}
static int getBits(UnpackCtx *uc, int count) { // rdd1bits
int bits = 0;
for (int i = 0; i < count; ++i) {
bits <<= 1;
if (nextBit(uc)) {
bits |= 1;
}
}
return bits;
}
static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { // getd3chr
int count = getBits(uc, bitsCount) + len + 1;
uc->size -= count;
if (uc->size < 0) {
count += uc->size;
uc->size = 0;
}
for (int i = 0; i < count; ++i) {
*(uc->dst - i) = (uint8)getBits(uc, 8);
}
uc->dst -= count;
}
static void copyReference(UnpackCtx *uc, int bitsCount, int count) { // copyd3bytes
uc->size -= count;
if (uc->size < 0) {
count += uc->size;
uc->size = 0;
}
const int offset = getBits(uc, bitsCount);
for (int i = 0; i < count; ++i) {
*(uc->dst - i) = *(uc->dst - i + offset);
}
uc->dst -= count;
}
bool bytekiller_unpack(uint8 *dst, int dstSize, const uint8 *src, int srcSize) {
UnpackCtx uc;
uc.src = src + srcSize - 4;
uc.size = READ_BE_UINT32(uc.src); uc.src -= 4;
if (uc.size > dstSize) {
warning("Unexpected unpack size %d, buffer size %d", uc.size, dstSize);
return false;
}
uc.dst = dst + uc.size - 1;
uc.crc = READ_BE_UINT32(uc.src); uc.src -= 4;
uc.bits = READ_BE_UINT32(uc.src); uc.src -= 4;
uc.crc ^= uc.bits;
do {
if (!nextBit(&uc)) {
if (!nextBit(&uc)) {
copyLiteral(&uc, 3, 0);
} else {
copyReference(&uc, 8, 2);
}
} else {
const int code = getBits(&uc, 2);
switch (code) {
case 3:
copyLiteral(&uc, 8, 8);
break;
case 2:
copyReference(&uc, 12, getBits(&uc, 8) + 1);
break;
case 1:
copyReference(&uc, 10, 4);
break;
case 0:
copyReference(&uc, 9, 3);
break;
}
}
} while (uc.size > 0);
assert(uc.size == 0);
return uc.crc == 0;
}
} // namespace Awe

34
engines/awe/unpack.h Normal file
View File

@@ -0,0 +1,34 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_UNPACK_H
#define AWE_UNPACK_H
#include "awe/intern.h"
namespace Awe {
extern bool bytekiller_unpack(uint8 *dst, int dstSize, const uint8 *src, int srcSize);
} // namespace Awe
#endif

624
engines/awe/video.cpp Normal file
View File

@@ -0,0 +1,624 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/surface.h"
#include "awe/video.h"
#include "awe/bitmap.h"
#include "awe/gfx.h"
#include "awe/resource.h"
#include "awe/resource_3do.h"
#include "awe/system_stub.h"
namespace Awe {
Video::Video(Resource *res) : _res(res) {
}
void Video::init() {
_nextPal = _currentPal = 0xFF;
_buffers[2] = getPagePtr(1);
_buffers[1] = getPagePtr(2);
setWorkPagePtr(0xFE);
_pData.byteSwap = (_res->getDataType() == DT_3DO);
}
void Video::setDefaultFont() {
_graphics->setFont(nullptr, 0, 0);
}
void Video::setFont(const uint8 *font) {
int w, h;
uint8 *buf = decode_bitmap(font, true, -1, &w, &h);
if (buf) {
_graphics->setFont(buf, w, h);
free(buf);
}
}
void Video::setHeads(const uint8 *src) {
int w, h;
uint8 *buf = decode_bitmap(src, true, 0xF06080, &w, &h);
if (buf) {
_graphics->setSpriteAtlas(buf, w, h, 2, 2);
free(buf);
_hasHeadSprites = true;
}
}
void Video::setDataBuffer(uint8 *dataBuf, uint16 offset) {
_dataBuf = dataBuf;
_pData.pc = dataBuf + offset;
}
void Video::drawShape(uint8 color, uint16 zoom, const Point *pt) {
uint8 i = _pData.fetchByte();
if (i >= 0xC0) {
if (color & 0x80) {
color = i & 0x3F;
}
fillPolygon(color, zoom, pt);
} else {
i &= 0x3F;
if (i == 1) {
warning("Video::drawShape() ec=0x%X (i != 2)", 0xF80);
} else if (i == 2) {
drawShapeParts(zoom, pt);
} else {
warning("Video::drawShape() ec=0x%X (i != 2)", 0xFBB);
}
}
}
void Video::drawShapePart3DO(int color, int part, const Point *pt) {
assert(part < (int)ARRAYSIZE(VERTICES_3DO));
const uint8 *vertices = VERTICES_3DO[part];
const int w = *vertices++;
const int h = *vertices++;
const int x = pt->x - w / 2;
const int y = pt->y - h / 2;
QuadStrip qs;
qs.numVertices = 2 * h;
assert(qs.numVertices < QuadStrip::MAX_VERTICES);
for (int i = 0; i < h; ++i) {
qs.vertices[i].x = x + *vertices++;
qs.vertices[i].y = y + i;
qs.vertices[2 * h - 1 - i].x = x + *vertices++;
qs.vertices[2 * h - 1 - i].y = y + i;
}
_graphics->drawQuadStrip(_buffers[0], color, &qs);
}
void Video::drawShape3DO(int color, int zoom, const Point *pt) {
const int code = _pData.fetchByte();
debugC(kDebugVideo, "Video::drawShape3DO() code=0x%x pt=%d,%d", code, pt->x, pt->y);
if (color == 0xFF) {
color = code & 31;
}
switch (code & 0xE0) {
case 0x00:
{
const int x0 = pt->x - _pData.fetchByte() * zoom / 64;
const int y0 = pt->y - _pData.fetchByte() * zoom / 64;
int count = _pData.fetchByte() + 1;
do {
uint16 offset = _pData.fetchWord();
Point po;
po.x = x0 + _pData.fetchByte() * zoom / 64;
po.y = y0 + _pData.fetchByte() * zoom / 64;
color = 0xFF;
if (offset & 0x8000) {
color = _pData.fetchByte();
const int num = _pData.fetchByte();
if (color & 0x80) {
drawShapePart3DO(color & 0xF, num, &po);
continue;
}
}
offset <<= 1;
uint8 *bak = _pData.pc;
_pData.pc = _dataBuf + offset;
drawShape3DO(color, zoom, &po);
_pData.pc = bak;
} while (--count != 0);
}
break;
case 0x20:
{ // rect
const int w = _pData.fetchByte() * zoom / 64;
const int h = _pData.fetchByte() * zoom / 64;
const int x1 = pt->x - w / 2;
const int y1 = pt->y - h / 2;
const int x2 = x1 + w;
const int y2 = y1 + h;
if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0) {
break;
}
QuadStrip qs;
qs.numVertices = 4;
qs.vertices[0].x = x1;
qs.vertices[0].y = y1;
qs.vertices[1].x = x1;
qs.vertices[1].y = y2;
qs.vertices[2].x = x2;
qs.vertices[2].y = y2;
qs.vertices[3].x = x2;
qs.vertices[3].y = y1;
_graphics->drawQuadStrip(_buffers[0], color, &qs);
}
break;
case 0x40:
{ // pixel
if (pt->x > 319 || pt->x < 0 || pt->y > 199 || pt->y < 0) {
break;
}
_graphics->drawPoint(_buffers[0], color, pt);
}
break;
case 0xC0:
{ // polygon
const int w = _pData.fetchByte() * zoom / 64;
const int h = _pData.fetchByte() * zoom / 64;
const int count = _pData.fetchByte();
QuadStrip qs;
qs.numVertices = count * 2;
assert(qs.numVertices < QuadStrip::MAX_VERTICES);
const int x0 = pt->x - w / 2;
const int y0 = pt->y - h / 2;
if (x0 > 319 || pt->x + w / 2 < 0 || y0 > 199 || pt->y + h / 2 < 0) {
break;
}
for (int i = 0, j = count * 2 - 1; i < count; ++i, --j) {
const int x1 = _pData.fetchByte() * zoom / 64;
const int x2 = _pData.fetchByte() * zoom / 64;
const int y = _pData.fetchByte() * zoom / 64;
qs.vertices[i].x = x0 + x2;
qs.vertices[(i + 1) % count].y = y0 + y;
qs.vertices[j].x = x0 + x1;
qs.vertices[count * 2 - 1 - (i + 1) % count].y = y0 + y;
}
_graphics->drawQuadStrip(_buffers[0], color, &qs);
}
break;
default:
warning("Video::drawShape3DO() unhandled code 0x%X", code);
break;
}
}
void Video::fillPolygon(uint16 color, uint16 zoom, const Point *pt) {
const uint8 *p = _pData.pc;
const uint16 bbw = (*p++) * zoom / 64;
const uint16 bbh = (*p++) * zoom / 64;
const int16 x1 = pt->x - bbw / 2;
const int16 x2 = pt->x + bbw / 2;
const int16 y1 = pt->y - bbh / 2;
const int16 y2 = pt->y + bbh / 2;
if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0)
return;
QuadStrip qs;
qs.numVertices = *p++;
if ((qs.numVertices & 1) != 0) {
warning("Unexpected number of vertices %d", qs.numVertices);
return;
}
assert(qs.numVertices < QuadStrip::MAX_VERTICES);
for (int i = 0; i < qs.numVertices; ++i) {
Point *v = &qs.vertices[i];
v->x = x1 + (*p++) * zoom / 64;
v->y = y1 + (*p++) * zoom / 64;
}
if (qs.numVertices == 4 && bbw == 0 && bbh <= 1) {
_graphics->drawPoint(_buffers[0], color, pt);
} else {
_graphics->drawQuadStrip(_buffers[0], color, &qs);
}
}
void Video::drawShapeParts(uint16 zoom, const Point *pgc) {
Point pt;
pt.x = pgc->x - _pData.fetchByte() * zoom / 64;
pt.y = pgc->y - _pData.fetchByte() * zoom / 64;
int16 n = _pData.fetchByte();
debugC(kDebugVideo, "Video::drawShapeParts n=%d", n);
for (; n >= 0; --n) {
uint16 offset = _pData.fetchWord();
Point po(pt);
po.x += _pData.fetchByte() * zoom / 64;
po.y += _pData.fetchByte() * zoom / 64;
uint16 color = 0xFF;
if (offset & 0x8000) {
color = _pData.fetchByte();
const int num = _pData.fetchByte();
if (Gfx::_is1991) {
if (!_hasHeadSprites && (color & 0x80) != 0) {
_graphics->drawSprite(_buffers[0], num, &po, color & 0x7F);
continue;
}
} else if (_hasHeadSprites && _displayHead) {
switch (num) {
case 0x4A:
{ // facing right
Point pos(po.x - 4, po.y - 7);
_graphics->drawSprite(_buffers[0], 0, &pos, color);
}
case 0x4D:
return;
case 0x4F:
{ // facing left
Point pos(po.x - 4, po.y - 7);
_graphics->drawSprite(_buffers[0], 1, &pos, color);
}
case 0x50:
return;
default:
break;
}
}
color &= 0x7F;
}
offset <<= 1;
uint8 *bak = _pData.pc;
_pData.pc = _dataBuf + offset;
drawShape(color, zoom, &po);
_pData.pc = bak;
}
}
static const int NTH_EDITION_STRINGS_COUNT = 157;
static const char *findString(const StrEntry *stringsTable, int id) {
for (const StrEntry *se = stringsTable; se->id != 0xFFFF; ++se) {
if (se->id == id) {
return se->str;
}
}
return nullptr;
}
void Video::drawString(uint8 color, uint16 x, uint16 y, uint16 strId) {
bool escapedChars = false;
const char *str = nullptr;
if (_res->getDataType() == DT_15TH_EDITION || _res->getDataType() == DT_20TH_EDITION) {
for (int i = 0; i < NTH_EDITION_STRINGS_COUNT; ++i) {
if (Video::STRINGS_ID_15TH[i] == strId) {
str = _res->getString(i);
if (str) {
escapedChars = true;
} else {
str = Video::STRINGS_TABLE_15TH[i];
}
break;
}
}
} else if (_res->getDataType() == DT_WIN31) {
str = _res->getString(strId);
} else if (_res->getDataType() == DT_3DO) {
str = findString(STRINGS_TABLE_3DO, strId);
} else if (_res->getDataType() == DT_ATARI_DEMO && strId == 0x194) {
str = _str0x194AtariDemo;
} else {
if (_res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI) {
str = findString(STRINGS_TABLE_AMIGA_CODES, strId);
}
if (!str) {
str = findString(_stringsTable, strId);
}
if (!str && _res->getDataType() == DT_DOS) {
str = findString(STRINGS_TABLE_DEMO, strId);
}
}
if (!str) {
warning("Unknown string id %d", strId);
return;
}
debugC(kDebugVideo, "drawString(%d, %d, %d, '%s')", color, x, y, str);
const uint16 xx = x;
const int len = strlen(str);
for (int i = 0; i < len; ++i) {
if (str[i] == '\n' || str[i] == '\r') {
y += 8;
x = xx;
} else if (str[i] == '\\' && escapedChars) {
++i;
if (i < len) {
switch (str[i]) {
case 'n':
y += 8;
x = xx;
break;
default:
break;
}
}
} else {
Point pt(x * 8, y);
_graphics->drawStringChar(_buffers[0], color, str[i], &pt);
++x;
}
}
}
uint8 Video::getPagePtr(uint8 page) {
uint8 p;
if (page <= 3) {
p = page;
} else {
switch (page) {
case 0xFF:
p = _buffers[2];
break;
case 0xFE:
p = _buffers[1];
break;
default:
p = 0; // XXX check
warning("Video::getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page);
break;
}
}
return p;
}
void Video::setWorkPagePtr(uint8 page) {
debugC(kDebugVideo, "Video::setWorkPagePtr(%d)", page);
_buffers[0] = getPagePtr(page);
}
void Video::fillPage(uint8 page, uint8 color) {
debugC(kDebugVideo, "Video::fillPage(%d, %d)", page, color);
_graphics->clearBuffer(getPagePtr(page), color);
}
void Video::copyPage(uint8 src, uint8 dst, int16 vscroll) {
debugC(kDebugVideo, "Video::copyPage(%d, %d)", src, dst);
if (src >= 0xFE || ((src &= ~0x40) & 0x80) == 0) { // no vscroll
_graphics->copyBuffer(getPagePtr(dst), getPagePtr(src));
} else {
const uint8 sl = getPagePtr(src & 3);
const uint8 dl = getPagePtr(dst);
if (sl != dl && vscroll >= -199 && vscroll <= 199) {
_graphics->copyBuffer(dl, sl, vscroll);
}
}
}
static void decode_amiga(const uint8 *src, uint8 *dst) {
static const int plane_size = 200 * 320 / 8;
for (int y = 0; y < 200; ++y) {
for (int x = 0; x < 320; x += 8) {
for (int b = 0; b < 8; ++b) {
const int mask = 1 << (7 - b);
uint8 color = 0;
for (int p = 0; p < 4; ++p) {
if (src[p * plane_size] & mask) {
color |= 1 << p;
}
}
*dst++ = color;
}
++src;
}
}
}
static void decode_atari(const uint8 *src, uint8 *dst) {
for (int y = 0; y < 200; ++y) {
for (int x = 0; x < 320; x += 16) {
for (int b = 0; b < 16; ++b) {
const int mask = 1 << (15 - b);
uint8 color = 0;
for (int p = 0; p < 4; ++p) {
if (READ_BE_UINT16(src + p * 2) & mask) {
color |= 1 << p;
}
}
*dst++ = color;
}
src += 8;
}
}
}
static void deinterlace555(const uint8 *src, int w, int h, uint16 *dst) {
for (int y = 0; y < h / 2; ++y) {
for (int x = 0; x < w; ++x) {
dst[x] = READ_BE_UINT16(src) & 0x7FFF; src += 2;
dst[w + x] = READ_BE_UINT16(src) & 0x7FFF; src += 2;
}
dst += w * 2;
}
}
static void yflip(const uint8 *src, int w, int h, uint8 *dst) {
for (int y = 0; y < h; ++y) {
memcpy(dst + (h - 1 - y) * w, src, w);
src += w;
}
}
void Video::scaleBitmap(const uint8 *src, int fmt) {
_graphics->drawBitmap(0, src, BITMAP_W, BITMAP_H, fmt);
}
void Video::copyBitmapPtr(const uint8 *src, uint32 size) {
if (_res->getDataType() == DT_DOS || _res->getDataType() == DT_AMIGA) {
decode_amiga(src, _tempBitmap);
scaleBitmap(_tempBitmap, FMT_CLUT);
} else if (_res->getDataType() == DT_ATARI) {
decode_atari(src, _tempBitmap);
scaleBitmap(_tempBitmap, FMT_CLUT);
} else if (_res->getDataType() == DT_WIN31) {
yflip(src, BITMAP_W, BITMAP_H, _tempBitmap);
scaleBitmap(_tempBitmap, FMT_CLUT);
} else if (_res->getDataType() == DT_3DO) {
deinterlace555(src, BITMAP_W, BITMAP_H, _bitmap555);
scaleBitmap((const uint8 *)_bitmap555, FMT_RGB555);
} else { // .BMP
if (Gfx::_is1991) {
const int w = READ_LE_UINT32(src + 0x12);
const int h = READ_LE_UINT32(src + 0x16);
if (w == BITMAP_W && h == BITMAP_H) {
const uint8 *data = src + READ_LE_UINT32(src + 0xA);
yflip(data, w, h, _tempBitmap);
scaleBitmap(_tempBitmap, FMT_CLUT);
}
} else {
int w, h;
uint8 *buf = decode_bitmap(src, false, -1, &w, &h);
if (buf) {
_graphics->drawBitmap(_buffers[0], buf, w, h, FMT_RGB);
free(buf);
}
}
}
}
static void readPaletteWin31(const uint8 *buf, int num, Color pal[16]) {
const uint8 *p = buf + num * 16 * sizeof(uint16);
for (int i = 0; i < 16; ++i) {
const uint16 index = READ_LE_UINT16(p); p += 2;
const uint32 color = READ_LE_UINT32(buf + 0xC04 + index * sizeof(uint32));
pal[i].r = color & 255;
pal[i].g = (color >> 8) & 255;
pal[i].b = (color >> 16) & 255;
}
}
static void readPalette3DO(const uint8 *buf, int num, Color pal[16]) {
const uint8 *p = buf + num * 16 * sizeof(uint16);
for (int i = 0; i < 16; ++i) {
const uint16 color = READ_BE_UINT16(p); p += 2;
const int r = (color >> 10) & 31;
const int g = (color >> 5) & 31;
const int b = color & 31;
pal[i].r = (r << 3) | (r >> 2);
pal[i].g = (g << 3) | (g >> 2);
pal[i].b = (b << 3) | (b >> 2);
}
}
static void readPaletteEGA(const uint8 *buf, int num, Color pal[16]) {
const uint8 *p = buf + num * 16 * sizeof(uint16);
p += 1024; // EGA colors are stored after VGA (Amiga)
for (int i = 0; i < 16; ++i) {
const uint16 color = READ_BE_UINT16(p); p += 2;
if (1) {
const uint8 *ega = &Video::PALETTE_EGA[3 * ((color >> 12) & 15)];
pal[i].r = ega[0];
pal[i].g = ega[1];
pal[i].b = ega[2];
} else { // lower 12 bits hold other colors
const uint8 r = (color >> 8) & 0xF;
const uint8 g = (color >> 4) & 0xF;
const uint8 b = color & 0xF;
pal[i].r = (r << 4) | r;
pal[i].g = (g << 4) | g;
pal[i].b = (b << 4) | b;
}
}
}
static void readPaletteAmiga(const uint8 *buf, int num, Color pal[16]) {
const uint8 *p = buf + num * 16 * sizeof(uint16);
for (int i = 0; i < 16; ++i) {
const uint16 color = READ_BE_UINT16(p); p += 2;
const uint8 r = (color >> 8) & 0xF;
const uint8 g = (color >> 4) & 0xF;
const uint8 b = color & 0xF;
pal[i].r = (r << 4) | r;
pal[i].g = (g << 4) | g;
pal[i].b = (b << 4) | b;
}
}
void Video::changePal(uint8 palNum) {
if (palNum < 32 && palNum != _currentPal) {
Color pal[16];
if (_res->getDataType() == DT_WIN31) {
readPaletteWin31(_res->_segVideoPal, palNum, pal);
} else if (_res->getDataType() == DT_3DO) {
readPalette3DO(_res->_segVideoPal, palNum, pal);
} else if (_res->getDataType() == DT_DOS && _useEGA) {
readPaletteEGA(_res->_segVideoPal, palNum, pal);
} else {
readPaletteAmiga(_res->_segVideoPal, palNum, pal);
}
_graphics->setPalette(pal, 16);
_currentPal = palNum;
}
}
void Video::updateDisplay(uint8 page, SystemStub *stub) {
debugC(kDebugVideo, "Video::updateDisplay(%d)", page);
if (page != 0xFE) {
if (page == 0xFF) {
SWAP(_buffers[1], _buffers[2]);
} else {
_buffers[1] = getPagePtr(page);
}
}
if (_nextPal != 0xFF) {
changePal(_nextPal);
_nextPal = 0xFF;
}
_graphics->drawBuffer(_buffers[1], stub);
}
void Video::setPaletteColor(uint8 color, int r, int g, int b) {
Color c;
c.r = r;
c.g = g;
c.b = b;
_graphics->setPalette(&c, 1);
}
void Video::drawRect(uint8 page, uint8 color, int x1, int y1, int x2, int y2) {
Point pt;
pt.x = x1;
pt.y = y1;
_graphics->drawRect(page, color, &pt, x2 - x1, y2 - y1);
}
void Video::drawBitmap3DO(const char *name, SystemStub *stub) {
assert(_res->getDataType() == DT_3DO);
int w, h;
uint16 *data = _res->_3do->loadShape555(name, &w, &h);
if (data) {
Graphics::Surface s;
s.setPixels(data);
s.w = s.pitch = w;
s.h = h;
s.format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
_graphics->drawBitmapOverlay(s, FMT_RGB555, stub);
free(data);
}
}
} // namespace Awe

103
engines/awe/video.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef AWE_VIDEO_H
#define AWE_VIDEO_H
#include "awe/intern.h"
namespace Awe {
struct StrEntry {
uint16 id;
const char *str;
};
struct Gfx;
struct Resource;
struct Scaler;
struct SystemStub;
struct Video {
enum {
BITMAP_W = 320,
BITMAP_H = 200
};
static const StrEntry STRINGS_TABLE_AMIGA_CODES[];
static const StrEntry STRINGS_TABLE_FR[];
static const StrEntry STRINGS_TABLE_ENG[];
static const StrEntry STRINGS_TABLE_ITA[];
static const StrEntry STRINGS_TABLE_DEMO[];
static const uint16 STRINGS_ID_15TH[];
static const char *STRINGS_TABLE_15TH[];
static const char *_str0x194AtariDemo;
static const StrEntry STRINGS_TABLE_3DO[];
static const char *NOTE_TEXT_3DO;
static const char *END_TEXT_3DO;
static const uint8 *VERTICES_3DO[201];
static const uint8 PALETTE_EGA[];
static bool _useEGA;
Resource *_res;
Gfx *_graphics = nullptr;
bool _hasHeadSprites = false;
bool _displayHead = true;
uint8 _nextPal = 0, _currentPal = 0;
uint8 _buffers[3] = { 0 };
Ptr _pData;
uint8 *_dataBuf = nullptr;
const StrEntry *_stringsTable = nullptr;
uint8 _tempBitmap[BITMAP_W * BITMAP_H] = { 0 };
uint16 _bitmap555[BITMAP_W * BITMAP_H] = { 0 };
Video(Resource *res);
~Video() {}
void init();
void setDefaultFont();
void setFont(const uint8 *font);
void setHeads(const uint8 *src);
void setDataBuffer(uint8 *dataBuf, uint16 offset);
void drawShape(uint8 color, uint16 zoom, const Point *pt);
void drawShapePart3DO(int color, int part, const Point *pt);
void drawShape3DO(int color, int zoom, const Point *pt);
void fillPolygon(uint16 color, uint16 zoom, const Point *pt);
void drawShapeParts(uint16 zoom, const Point *pt);
void drawString(uint8 color, uint16 x, uint16 y, uint16 strId);
uint8 getPagePtr(uint8 page);
void setWorkPagePtr(uint8 page);
void fillPage(uint8 page, uint8 color);
void copyPage(uint8 src, uint8 dst, int16 vscroll);
void scaleBitmap(const uint8 *src, int fmt);
void copyBitmapPtr(const uint8 *src, uint32 size = 0);
void changePal(uint8 pal);
void updateDisplay(uint8 page, SystemStub *stub);
void setPaletteColor(uint8 color, int r, int g, int b);
void drawRect(uint8 page, uint8 color, int x1, int y1, int x2, int y2);
void drawBitmap3DO(const char *name, SystemStub *stub);
};
} // namespace Awe
#endif