Initial commit
This commit is contained in:
1
engines/awe/POTFILES
Normal file
1
engines/awe/POTFILES
Normal file
@@ -0,0 +1 @@
|
||||
engines/awe/metaengine.cpp
|
||||
139
engines/awe/aifc_player.cpp
Normal file
139
engines/awe/aifc_player.cpp
Normal 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
51
engines/awe/aifc_player.h
Normal 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
221
engines/awe/awe.cpp
Normal 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
65
engines/awe/awe.h
Normal 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
92
engines/awe/bitmap.cpp
Normal 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
33
engines/awe/bitmap.h
Normal 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
|
||||
3
engines/awe/configure.engine
Normal file
3
engines/awe/configure.engine
Normal 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
4
engines/awe/credits.pl
Normal 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
45
engines/awe/detection.cpp
Normal 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
90
engines/awe/detection.h
Normal 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
|
||||
240
engines/awe/detection_tables.h
Normal file
240
engines/awe/detection_tables.h
Normal 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
264
engines/awe/engine.cpp
Normal 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
83
engines/awe/engine.h
Normal 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
96
engines/awe/gfx.h
Normal 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
839
engines/awe/graphics_gl.cpp
Normal 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
|
||||
507
engines/awe/graphics_soft.cpp
Normal file
507
engines/awe/graphics_soft.cpp
Normal 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
124
engines/awe/intern.h
Normal 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
112
engines/awe/metaengine.cpp
Normal 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
61
engines/awe/metaengine.h
Normal 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
33
engines/awe/module.mk
Normal 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
152
engines/awe/pak.cpp
Normal 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
57
engines/awe/pak.h
Normal 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
695
engines/awe/resource.cpp
Normal 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
172
engines/awe/resource.h
Normal 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
|
||||
199
engines/awe/resource_3do.cpp
Normal file
199
engines/awe/resource_3do.cpp
Normal 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
|
||||
45
engines/awe/resource_3do.h
Normal file
45
engines/awe/resource_3do.h
Normal 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
|
||||
586
engines/awe/resource_nth.cpp
Normal file
586
engines/awe/resource_nth.cpp
Normal 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
|
||||
48
engines/awe/resource_nth.h
Normal file
48
engines/awe/resource_nth.h
Normal 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
|
||||
366
engines/awe/resource_win31.cpp
Normal file
366
engines/awe/resource_win31.cpp
Normal 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
|
||||
60
engines/awe/resource_win31.h
Normal file
60
engines/awe/resource_win31.h
Normal 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
927
engines/awe/script.cpp
Normal 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
137
engines/awe/script.h
Normal 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
274
engines/awe/sfx_player.cpp
Normal 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
102
engines/awe/sfx_player.h
Normal 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
630
engines/awe/sound.cpp
Normal 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
122
engines/awe/sound.h
Normal 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
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
264
engines/awe/system_stub.cpp
Normal 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
86
engines/awe/system_stub.h
Normal 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
125
engines/awe/unpack.cpp
Normal 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
34
engines/awe/unpack.h
Normal 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
624
engines/awe/video.cpp
Normal 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
103
engines/awe/video.h
Normal 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
|
||||
Reference in New Issue
Block a user