Initial commit
This commit is contained in:
349
engines/freescape/games/driller/amiga.cpp
Normal file
349
engines/freescape/games/driller/amiga.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/* 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 "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
void DrillerEngine::loadAssetsAmigaFullGame() {
|
||||
Common::File file;
|
||||
if (_variant & GF_AMIGA_RETAIL) {
|
||||
file.open("driller");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'driller' executable for Amiga");
|
||||
|
||||
_border = loadAndConvertNeoImage(&file, 0x137f4);
|
||||
_title = loadAndConvertNeoImage(&file, 0xce);
|
||||
|
||||
loadFonts(&file, 0x8940);
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, &file, 0x8940 + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
|
||||
loadMessagesFixedSize(&file, 0xc66e, 14, 20);
|
||||
loadGlobalObjects(&file, 0xbd62, 8);
|
||||
load8bitBinary(&file, 0x29c16, 16);
|
||||
loadPalettes(&file, 0x297d4);
|
||||
loadSoundsFx(&file, 0x30e80, 25);
|
||||
} else if (_variant & GF_AMIGA_BUDGET) {
|
||||
file.open("lift.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'lift.neo' file");
|
||||
|
||||
_title = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("console.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'console.neo' file");
|
||||
|
||||
_border = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("driller");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'driller' executable for Amiga");
|
||||
|
||||
loadFonts(&file, 0xa62);
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, &file, 0xa62 + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
|
||||
loadMessagesFixedSize(&file, 0x499a, 14, 20);
|
||||
loadGlobalObjects(&file, 0x4098, 8);
|
||||
load8bitBinary(&file, 0x21a3e, 16);
|
||||
loadPalettes(&file, 0x215fc);
|
||||
|
||||
file.close();
|
||||
file.open("soundfx");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'soundfx' executable for Amiga");
|
||||
|
||||
loadSoundsFx(&file, 0, 25);
|
||||
} else
|
||||
error("Invalid or unknown Amiga release");
|
||||
|
||||
|
||||
for (auto &area : _areaMap) {
|
||||
// Center and pad each area name so we do not have to do it at each frame
|
||||
area._value->_name = centerAndPadString(area._value->_name, 14);
|
||||
}
|
||||
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_0"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_1"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_2"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_3"));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator"));
|
||||
|
||||
for (auto &it : _indicators)
|
||||
it->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsAmigaDemo() {
|
||||
Common::File file;
|
||||
file.open("lift.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'lift.neo' file");
|
||||
|
||||
_title = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("console.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'console.neo' file");
|
||||
|
||||
_border = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("demo.cmd");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'demo.cmd' file");
|
||||
|
||||
loadDemoData(&file, 0, 0x1000);
|
||||
|
||||
file.close();
|
||||
file.open("driller");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'driller' file");
|
||||
|
||||
if (_variant & GF_AMIGA_MAGAZINE_DEMO) {
|
||||
loadMessagesFixedSize(&file, 0x3df0, 14, 20);
|
||||
loadGlobalObjects(&file, 0x3ba6, 8);
|
||||
_demoMode = false;
|
||||
|
||||
loadFonts(&file, 0xa62);
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, &file, 0xa62 + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
} else {
|
||||
loadFonts(&file, 0xa30);
|
||||
loadMessagesFixedSize(&file, 0x3960, 14, 20);
|
||||
loadGlobalObjects(&file, 0x3716, 8);
|
||||
}
|
||||
|
||||
file.close();
|
||||
file.open("data");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'data' file");
|
||||
|
||||
load8bitBinary(&file, 0x442, 16);
|
||||
loadPalettes(&file, 0x0);
|
||||
|
||||
file.close();
|
||||
file.open("soundfx");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'soundfx' executable for Amiga");
|
||||
|
||||
loadSoundsFx(&file, 0, 25);
|
||||
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_0"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_1"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_2"));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_3"));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator"));
|
||||
|
||||
for (auto &it : _indicators)
|
||||
it->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
|
||||
/*
|
||||
The following function contains specific UI code for both Amiga and AtariST
|
||||
*/
|
||||
|
||||
void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
|
||||
uint32 white = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
uint32 yellow = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0x55);
|
||||
uint32 brownish = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x9E, 0x80, 0x20);
|
||||
uint32 brown = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x7E, 0x60, 0x19);
|
||||
uint32 red = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xE0, 0x00, 0x00);
|
||||
uint32 redish = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xE0, 0x60, 0x20);
|
||||
uint32 primaryFontColor = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xA0, 0x80, 0x00);
|
||||
uint32 secondaryFontColor = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x60, 0x40, 0x00);
|
||||
uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
|
||||
uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0x00, 0x00, 0x00);
|
||||
|
||||
int score = _gameStateVars[k8bitVariableScore];
|
||||
Common::String coords;
|
||||
|
||||
// It seems that some demos will not include the complete font
|
||||
if (!isDemo() || (_variant & GF_AMIGA_MAGAZINE_DEMO) || (_variant & GF_ATARI_MAGAZINE_DEMO)) {
|
||||
|
||||
drawString(kDrillerFontSmall, ":", 38, 18, white, white, transparent, surface); // ":" is the next character to "9" representing "x"
|
||||
coords = Common::String::format("%04d", 2 * int(_position.x()));
|
||||
drawString(kDrillerFontSmall, coords, 47, 18, white, transparent, transparent, surface);
|
||||
|
||||
drawString(kDrillerFontSmall, ";", 37, 26, white, white, transparent, surface); // ";" is the next character to ":" representing "y"
|
||||
coords = Common::String::format("%04d", 2 * int(_position.z())); // Coords y and z are swapped!
|
||||
drawString(kDrillerFontSmall, coords, 47, 26, white, transparent, transparent, surface);
|
||||
|
||||
drawString(kDrillerFontSmall, "<", 37, 34, white, white, transparent, surface); // "<" is the next character to ";" representing "z"
|
||||
coords = Common::String::format("%04d", 2 * int(_position.y())); // Coords y and z are swapped!
|
||||
drawString(kDrillerFontSmall, coords, 47, 34, white, transparent, transparent, surface);
|
||||
}
|
||||
|
||||
drawStringInSurface(_currentArea->_name, 189, 185, primaryFontColor, secondaryFontColor, black, surface);
|
||||
drawStringInSurface(Common::String::format("%07d", score), 241, 129, primaryFontColor, secondaryFontColor, black, surface);
|
||||
|
||||
int seconds, minutes, hours;
|
||||
getTimeFromCountdown(seconds, minutes, hours);
|
||||
drawStringInSurface(Common::String::format("%02d:", hours), 210, 7, primaryFontColor, secondaryFontColor, black, surface);
|
||||
drawStringInSurface(Common::String::format("%02d:", minutes), 230, 7, primaryFontColor, secondaryFontColor, black, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", seconds), 254, 7, primaryFontColor, secondaryFontColor, black, surface);
|
||||
|
||||
Common::String message;
|
||||
int deadline;
|
||||
getLatestMessages(message, deadline);
|
||||
if (deadline <= _countdown) {
|
||||
drawStringInSurface(message, 188, 177, yellow, secondaryFontColor, black, surface);
|
||||
_temporaryMessages.push_back(message);
|
||||
_temporaryMessageDeadlines.push_back(deadline);
|
||||
} else {
|
||||
if (_currentArea->_gasPocketRadius == 0)
|
||||
message = _messagesList[2];
|
||||
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
||||
message = _messagesList[0];
|
||||
else
|
||||
message = _messagesList[1];
|
||||
|
||||
drawStringInSurface(message, 188, 177, primaryFontColor, secondaryFontColor, black, surface);
|
||||
}
|
||||
|
||||
int energy = _gameStateVars[k8bitVariableEnergy];
|
||||
int shield = _gameStateVars[k8bitVariableShield];
|
||||
|
||||
if (shield >= 0) {
|
||||
Common::Rect shieldBar;
|
||||
shieldBar = Common::Rect(11, 178, 74 - (_maxShield - shield), 184);
|
||||
surface->fillRect(shieldBar, brown);
|
||||
|
||||
if (shield > 11)
|
||||
shieldBar = Common::Rect(11, 178, 25, 184);
|
||||
else
|
||||
shieldBar = Common::Rect(11, 178, 74 - (_maxShield - shield), 184);
|
||||
surface->fillRect(shieldBar, red);
|
||||
|
||||
shieldBar = Common::Rect(11, 179, 74 - (_maxShield - shield), 183);
|
||||
surface->fillRect(shieldBar, brownish);
|
||||
|
||||
if (shield > 11)
|
||||
shieldBar = Common::Rect(11, 179, 25, 183);
|
||||
else
|
||||
shieldBar = Common::Rect(11, 179, 74 - (_maxShield - shield), 183);
|
||||
surface->fillRect(shieldBar, redish);
|
||||
|
||||
shieldBar = Common::Rect(11, 180, 74 - (_maxShield - shield), 182);
|
||||
surface->fillRect(shieldBar, yellow);
|
||||
}
|
||||
|
||||
if (energy >= 0) {
|
||||
Common::Rect energyBar;
|
||||
energyBar = Common::Rect(11, 186, 74 - (_maxEnergy - energy), 192);
|
||||
surface->fillRect(energyBar, brown);
|
||||
|
||||
if (energy > 11)
|
||||
energyBar = Common::Rect(11, 186, 24, 192);
|
||||
else
|
||||
energyBar = Common::Rect(11, 186, 74 - (_maxEnergy - energy), 192);
|
||||
surface->fillRect(energyBar, red);
|
||||
|
||||
energyBar = Common::Rect(11, 187, 74 - (_maxEnergy - energy), 191);
|
||||
surface->fillRect(energyBar, brownish);
|
||||
|
||||
if (energy > 11)
|
||||
energyBar = Common::Rect(11, 187, 24, 191);
|
||||
else
|
||||
energyBar = Common::Rect(11, 187, 74 - (_maxEnergy - energy), 191);
|
||||
surface->fillRect(energyBar, redish);
|
||||
|
||||
energyBar = Common::Rect(11, 188, 74 - (_maxEnergy - energy), 190);
|
||||
surface->fillRect(energyBar, yellow);
|
||||
}
|
||||
|
||||
if (_indicators.size() > 0) {
|
||||
if (_flyMode)
|
||||
surface->copyRectToSurface(*_indicators[4], 106, 128, Common::Rect(_indicators[1]->w, _indicators[1]->h));
|
||||
else
|
||||
surface->copyRectToSurface(*_indicators[_playerHeightNumber], 106, 128, Common::Rect(_indicators[1]->w, _indicators[1]->h));
|
||||
}
|
||||
}
|
||||
|
||||
void DrillerEngine::drawString(const DrillerFontSize size, const Common::String &str, int x, int y, uint32 primaryColor, uint32 secondaryColor, uint32 backColor, Graphics::Surface *surface) {
|
||||
if (!_fontLoaded)
|
||||
return;
|
||||
|
||||
Font *font = nullptr;
|
||||
|
||||
if (size == kDrillerFontNormal) {
|
||||
font = &_font;
|
||||
} else if (size == kDrillerFontSmall) {
|
||||
font = &_fontSmall;
|
||||
} else {
|
||||
error("Invalid font size %d", size);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String ustr = str;
|
||||
ustr.toUppercase();
|
||||
font->setBackground(backColor);
|
||||
font->setSecondaryColor(secondaryColor);
|
||||
font->drawString(surface, ustr, x, y, _screenW, primaryColor);
|
||||
}
|
||||
|
||||
void DrillerEngine::initAmigaAtari() {
|
||||
_viewArea = Common::Rect(36, 16, 284, 118);
|
||||
|
||||
_moveFowardArea = Common::Rect(184, 125, 199, 144);
|
||||
_moveLeftArea = Common::Rect(161, 145, 174, 164);
|
||||
_moveRightArea = Common::Rect(207, 145, 222, 164);
|
||||
_moveBackArea = Common::Rect(184, 152, 199, 171);
|
||||
_moveUpArea = Common::Rect(231, 145, 246, 164);
|
||||
_moveDownArea = Common::Rect(254, 145, 269, 164);
|
||||
_deployDrillArea = Common::Rect(284, 145, 299, 166);
|
||||
_infoScreenArea = Common::Rect(125, 172, 152, 197);
|
||||
_saveGameArea = Common::Rect(9, 145, 39, 154);
|
||||
_loadGameArea = Common::Rect(9, 156, 39, 164);
|
||||
|
||||
_borderExtra = nullptr;
|
||||
_borderExtraTexture = nullptr;
|
||||
|
||||
_soundIndexShoot = 1;
|
||||
_soundIndexCollide = 19;
|
||||
_soundIndexStepDown = 19;
|
||||
_soundIndexStepUp = 19;
|
||||
_soundIndexAreaChange = 5;
|
||||
_soundIndexHit = 2;
|
||||
_soundIndexFall = 25;
|
||||
_soundIndexFallen = 11;
|
||||
_soundIndexForceEndGame = 11;
|
||||
_soundIndexNoShield = 11;
|
||||
_soundIndexNoEnergy = 11;
|
||||
_soundIndexTimeout = 11;
|
||||
_soundIndexCrushed = 11;
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
386
engines/freescape/games/driller/atari.cpp
Normal file
386
engines/freescape/games/driller/atari.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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 "common/memstream.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
namespace {
|
||||
// A simple implementation of memmem, which is a non-standard GNU extension.
|
||||
const void *local_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) {
|
||||
if (needle_len == 0) {
|
||||
return haystack;
|
||||
}
|
||||
if (haystack_len < needle_len) {
|
||||
return nullptr;
|
||||
}
|
||||
const char *h = (const char *)haystack;
|
||||
for (size_t i = 0; i <= haystack_len - needle_len; ++i) {
|
||||
if (memcmp(h + i, needle, needle_len) == 0) {
|
||||
return h + i;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Common::SeekableReadStream *DrillerEngine::decryptFileAtariVirtualWorlds(const Common::Path &filename) {
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
error("Failed to open %s", filename.toString().c_str());
|
||||
}
|
||||
const int size = file.size();
|
||||
byte *data = (byte *)malloc(size);
|
||||
file.read(data, size);
|
||||
|
||||
int start = 0;
|
||||
int valid_offset = -1;
|
||||
int chunk_size = 0;
|
||||
|
||||
while (true) {
|
||||
const byte *found = (const byte *)local_memmem(data + start, size - start, "CBCP", 4);
|
||||
if (!found) break;
|
||||
|
||||
int idx = found - data;
|
||||
if (idx + 8 <= size) {
|
||||
int sz = READ_BE_UINT32(data + idx + 4);
|
||||
if (sz > 0 && sz < size + 0x20000) {
|
||||
valid_offset = idx;
|
||||
chunk_size = sz;
|
||||
}
|
||||
}
|
||||
start = idx + 1;
|
||||
}
|
||||
|
||||
if (valid_offset == -1) {
|
||||
error("No valid CBCP chunk found in %s", filename.toString().c_str());
|
||||
}
|
||||
|
||||
const byte *payload = data + valid_offset + 8;
|
||||
const int payload_size = chunk_size;
|
||||
|
||||
if (payload_size < 12) {
|
||||
error("Payload too short in %s", filename.toString().c_str());
|
||||
}
|
||||
|
||||
uint32 bit_buf_init = READ_BE_UINT32(payload + payload_size - 12);
|
||||
uint32 checksum_init = READ_BE_UINT32(payload + payload_size - 8);
|
||||
uint32 decoded_size = READ_BE_UINT32(payload + payload_size - 4);
|
||||
|
||||
byte *out_buffer = (byte *)malloc(decoded_size);
|
||||
int dst_idx = decoded_size;
|
||||
|
||||
struct BitStream {
|
||||
const byte *_src_data;
|
||||
int _src_idx;
|
||||
uint32 _bit_buffer;
|
||||
uint32 _checksum;
|
||||
int _refill_carry;
|
||||
|
||||
BitStream(const byte *src_data, int start_idx, uint32 bit_buffer, uint32 checksum) :
|
||||
_src_data(src_data), _src_idx(start_idx), _bit_buffer(bit_buffer), _checksum(checksum), _refill_carry(0) {}
|
||||
|
||||
void refill() {
|
||||
if (_src_idx < 0) {
|
||||
_refill_carry = 0;
|
||||
_bit_buffer = 0x80000000;
|
||||
return;
|
||||
}
|
||||
uint32 val = READ_BE_UINT32(_src_data + _src_idx);
|
||||
_src_idx -= 4;
|
||||
_checksum ^= val;
|
||||
_refill_carry = val & 1;
|
||||
_bit_buffer = (val >> 1) | 0x80000000;
|
||||
}
|
||||
|
||||
int getBits(int count) {
|
||||
uint32 result = 0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int carry = _bit_buffer & 1;
|
||||
_bit_buffer >>= 1;
|
||||
if (_bit_buffer == 0) {
|
||||
refill();
|
||||
carry = _refill_carry;
|
||||
}
|
||||
result = (result << 1) | carry;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
int src_idx = payload_size - 16;
|
||||
uint32 checksum = checksum_init ^ bit_buf_init;
|
||||
BitStream bs(payload, src_idx, bit_buf_init, checksum);
|
||||
|
||||
while (dst_idx > 0) {
|
||||
if (bs.getBits(1) == 0) {
|
||||
if (bs.getBits(1) == 1) {
|
||||
int offset = bs.getBits(8);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
dst_idx--;
|
||||
if (dst_idx >= 0) {
|
||||
out_buffer[dst_idx] = out_buffer[dst_idx + offset];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int count = bs.getBits(3) + 1;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst_idx--;
|
||||
if (dst_idx >= 0) {
|
||||
out_buffer[dst_idx] = bs.getBits(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int tag = bs.getBits(2);
|
||||
if (tag == 3) {
|
||||
int count = bs.getBits(8) + 9;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst_idx--;
|
||||
if (dst_idx >= 0) {
|
||||
out_buffer[dst_idx] = bs.getBits(8);
|
||||
}
|
||||
}
|
||||
} else if (tag == 2) {
|
||||
int length = bs.getBits(8) + 1;
|
||||
int offset = bs.getBits(12);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
dst_idx--;
|
||||
if (dst_idx >= 0) {
|
||||
out_buffer[dst_idx] = out_buffer[dst_idx + offset];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int bits_offset = 9 + tag;
|
||||
int length = 3 + tag;
|
||||
int offset = bs.getBits(bits_offset);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
dst_idx--;
|
||||
if (dst_idx >= 0) {
|
||||
out_buffer[dst_idx] = out_buffer[dst_idx + offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
return new Common::MemoryReadStream(out_buffer, decoded_size);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *DrillerEngine::decryptFileAtari(const Common::Path &filename) {
|
||||
Common::File file;
|
||||
file.open(filename);
|
||||
if (!file.isOpen())
|
||||
error("Failed to open %s", filename.toString().c_str());
|
||||
|
||||
int size = file.size();
|
||||
byte *encryptedBuffer = (byte *)malloc(size);
|
||||
file.read(encryptedBuffer, size);
|
||||
file.close();
|
||||
|
||||
byte *a6 = encryptedBuffer + 0x118;
|
||||
byte *a5 = encryptedBuffer + size - 4;
|
||||
uint64 key = 0xb9f11bce;
|
||||
|
||||
while (a6 <= a5) {
|
||||
uint64 d0 = (a6[0] << 24) | (a6[1] << 16) | (a6[2] << 8) | a6[3];
|
||||
d0 += key;
|
||||
d0 = uint32(d0);
|
||||
|
||||
a6[0] = byte((d0 >> 24) & 0xFF);
|
||||
a6[1] = byte((d0 >> 16) & 0xFF);
|
||||
a6[2] = byte((d0 >> 8) & 0xFF);
|
||||
a6[3] = byte(d0 & 0xFF);
|
||||
|
||||
key += 0x51684624;
|
||||
key = uint32(key);
|
||||
a6 += 4;
|
||||
}
|
||||
|
||||
return (new Common::MemoryReadStream(encryptedBuffer, size));
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsAtariFullGame() {
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
if (_variant & GF_ATARI_RETAIL) {
|
||||
stream = decryptFileAtari("x.prg");
|
||||
|
||||
_border = loadAndConvertNeoImage(stream, 0x14b96);
|
||||
_borderExtra = loadAndConvertNeoImage(stream, 0x1c916);
|
||||
_title = loadAndConvertNeoImage(stream, 0x3f6);
|
||||
|
||||
loadFonts(stream, 0x8a92);
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, stream, 0x8a92 + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
|
||||
loadMessagesFixedSize(stream, 0xda22, 14, 20);
|
||||
loadGlobalObjects(stream, 0xd116, 8);
|
||||
load8bitBinary(stream, 0x2afb8, 16);
|
||||
loadPalettes(stream, 0x2ab76);
|
||||
loadSoundsFx(stream, 0x30da6 + 0x147c, 25);
|
||||
} else if (_variant & GF_ATARI_BUDGET) {
|
||||
Common::File file;
|
||||
file.open("x.prg");
|
||||
|
||||
if (!file.isOpen()) {
|
||||
stream = decryptFileAtariVirtualWorlds("dril.all");
|
||||
} else
|
||||
stream = &file;
|
||||
|
||||
if (isSpaceStationOblivion()) {
|
||||
_border = loadAndConvertNeoImage(&file, 0x13544);
|
||||
byte palette[16 * 3];
|
||||
for (int i = 0; i < 16; i++) { // gray scale palette
|
||||
palette[i * 3 + 0] = i * (255 / 16);
|
||||
palette[i * 3 + 1] = i * (255 / 16);
|
||||
palette[i * 3 + 2] = i * (255 / 16);
|
||||
}
|
||||
_title = loadAndConvertNeoImage(&file, 0x10, palette);
|
||||
|
||||
loadFonts(&file, 0x8a32 - 0x1d6);
|
||||
loadMessagesFixedSize(&file, 0xc5d8 - 0x1da, 14, 20);
|
||||
loadGlobalObjects(&file, 0xbccc - 0x1da, 8);
|
||||
load8bitBinary(&file, 0x29b3c - 0x1d6, 16);
|
||||
loadPalettes(&file, 0x296fa - 0x1d6);
|
||||
loadSoundsFx(&file, 0x30da6 - 0x1d6, 25);
|
||||
} else {
|
||||
_border = loadAndConvertNeoImage(stream, 0x1371a);
|
||||
_title = loadAndConvertNeoImage(stream, 0x396);
|
||||
|
||||
loadFonts(stream, 0x8a32);
|
||||
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, stream, 0x8a32 + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
|
||||
loadMessagesFixedSize(stream, 0xc5d8, 14, 20);
|
||||
loadGlobalObjects(stream, 0xbccc, 8);
|
||||
load8bitBinary(stream, 0x29b3c, 16);
|
||||
loadPalettes(stream, 0x296fa);
|
||||
loadSoundsFx(stream, 0x30da6, 25);
|
||||
}
|
||||
} else
|
||||
error("Unknown Atari ST Driller variant");
|
||||
|
||||
for (auto &area : _areaMap) {
|
||||
// Center and pad each area name so we do not have to do it at each frame
|
||||
area._value->_name = centerAndPadString(area._value->_name, 14);
|
||||
}
|
||||
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_0_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_1_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_2_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_3_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator_Amiga", false));
|
||||
|
||||
for (auto &it : _indicators)
|
||||
it->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsAtariDemo() {
|
||||
Common::File file;
|
||||
file.open("lift.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'lift.neo' file");
|
||||
|
||||
_title = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("console.neo");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'console.neo' file");
|
||||
|
||||
_border = loadAndConvertNeoImage(&file, 0);
|
||||
|
||||
file.close();
|
||||
file.open("demo.cmd");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'demo.cmd' file");
|
||||
|
||||
loadDemoData(&file, 0, 0x1000);
|
||||
|
||||
file.close();
|
||||
if (_variant & GF_ATARI_MAGAZINE_DEMO) {
|
||||
file.open("auto_x.prg");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'auto_x.prg' file");
|
||||
_demoMode = false;
|
||||
} else {
|
||||
file.open("x.prg");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'x.prg' file");
|
||||
}
|
||||
|
||||
if (_variant & GF_ATARI_MAGAZINE_DEMO) {
|
||||
loadMessagesFixedSize(&file, 0x40d2, 14, 20);
|
||||
loadGlobalObjects(&file, 0x3e88, 8);
|
||||
|
||||
loadFonts(&file, 0x7ee);
|
||||
Common::Array<Graphics::ManagedSurface *> chars;
|
||||
chars = getCharsAmigaAtariInternal(8, 8, -3, 33, 32, &file, 0x7ee + 112 * 33 + 1, 100);
|
||||
_fontSmall = Font(chars);
|
||||
_fontSmall.setCharWidth(5);
|
||||
} else {
|
||||
loadFonts(&file, 0x7bc);
|
||||
loadMessagesFixedSize(&file, 0x3b90, 14, 20);
|
||||
loadGlobalObjects(&file, 0x3946, 8);
|
||||
}
|
||||
|
||||
file.close();
|
||||
file.open("data");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'data' file");
|
||||
|
||||
load8bitBinary(&file, 0x442, 16);
|
||||
loadPalettes(&file, 0x0);
|
||||
|
||||
file.close();
|
||||
file.open("soundfx");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'soundfx' executable for AtariST demo");
|
||||
|
||||
loadSoundsFx(&file, 0, 25);
|
||||
|
||||
for (auto &area : _areaMap) {
|
||||
// Center and pad each area name so we do not have to do it at each frame
|
||||
area._value->_name = centerAndPadString(area._value->_name, 14);
|
||||
}
|
||||
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_0_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_1_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_2_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator_3_Amiga", false));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator_Amiga", false));
|
||||
|
||||
for (auto &it : _indicators)
|
||||
it->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
249
engines/freescape/games/driller/c64.cpp
Normal file
249
engines/freescape/games/driller/c64.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/* 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 "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
extern byte kC64Palette[16][3];
|
||||
|
||||
void DrillerEngine::initC64() {
|
||||
_viewArea = Common::Rect(32, 16, 288, 120);
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsC64FullGame() {
|
||||
Common::File file;
|
||||
if (_targetName.hasPrefix("spacestationoblivion")) {
|
||||
file.open("spacestationoblivion.c64.data");
|
||||
loadMessagesFixedSize(&file, 0x167a, 14, 20);
|
||||
//loadFonts(&file, 0xae54);
|
||||
load8bitBinary(&file, 0x8e02, 4);
|
||||
loadGlobalObjects(&file, 0x1855, 8);
|
||||
} else if (_targetName.hasPrefix("driller")) {
|
||||
file.open("driller.c64.data");
|
||||
|
||||
if (_variant) {
|
||||
loadMessagesFixedSize(&file, 0x167a, 14, 20);
|
||||
loadGlobalObjects(&file, 0x1855, 8);
|
||||
loadFonts(&file, 0x402);
|
||||
load8bitBinary(&file, 0x8b04, 16);
|
||||
/*} else if (_variant & GF_C64_BUDGET) {
|
||||
//loadFonts(&file, 0x402);
|
||||
load8bitBinary(&file, 0x7df7, 16);
|
||||
loadMessagesFixedSize(&file, 0x1399, 14, 20);
|
||||
loadGlobalObjects(&file, 0x150a, 8);*/
|
||||
} else
|
||||
error("Unknown C64 variant %x", _variant);
|
||||
|
||||
// The color map from the data is not correct,
|
||||
// so we'll just hardcode the one that we found in the executable
|
||||
|
||||
for (int i = 0; i < 15; i++) {
|
||||
_colorMap[i][0] = 0;
|
||||
_colorMap[i][1] = 0;
|
||||
_colorMap[i][2] = 0;
|
||||
_colorMap[i][3] = 0;
|
||||
}
|
||||
|
||||
_colorMap[1][0] = 0x55;
|
||||
_colorMap[1][1] = 0x55;
|
||||
_colorMap[1][2] = 0x55;
|
||||
_colorMap[1][3] = 0x55;
|
||||
|
||||
_colorMap[2][0] = 0xaa;
|
||||
_colorMap[2][1] = 0xaa;
|
||||
_colorMap[2][2] = 0xaa;
|
||||
_colorMap[2][3] = 0xaa;
|
||||
|
||||
_colorMap[3][0] = 0xff;
|
||||
_colorMap[3][1] = 0xff;
|
||||
_colorMap[3][2] = 0xff;
|
||||
_colorMap[3][3] = 0xff;
|
||||
|
||||
_colorMap[4][0] = 0x44;
|
||||
_colorMap[4][1] = 0x11;
|
||||
_colorMap[4][2] = 0x44;
|
||||
_colorMap[4][3] = 0x11;
|
||||
|
||||
_colorMap[5][0] = 0x88;
|
||||
_colorMap[5][1] = 0x22;
|
||||
_colorMap[5][2] = 0x88;
|
||||
_colorMap[5][3] = 0x22;
|
||||
|
||||
_colorMap[6][0] = 0xcc;
|
||||
_colorMap[6][1] = 0x33;
|
||||
_colorMap[6][2] = 0xcc;
|
||||
_colorMap[6][3] = 0x33;
|
||||
|
||||
_colorMap[7][0] = 0x66;
|
||||
_colorMap[7][1] = 0x99;
|
||||
_colorMap[7][2] = 0x66;
|
||||
_colorMap[7][3] = 0x99;
|
||||
|
||||
_colorMap[8][0] = 0x77;
|
||||
_colorMap[8][1] = 0xdd;
|
||||
_colorMap[8][2] = 0x77;
|
||||
_colorMap[8][3] = 0xdd;
|
||||
|
||||
_colorMap[9][0] = 0xbb;
|
||||
_colorMap[9][1] = 0xee;
|
||||
_colorMap[9][2] = 0xbb;
|
||||
_colorMap[9][3] = 0xee;
|
||||
|
||||
_colorMap[10][0] = 0x5a;
|
||||
_colorMap[10][1] = 0xa5;
|
||||
_colorMap[10][2] = 0x5a;
|
||||
_colorMap[10][3] = 0xa5;
|
||||
|
||||
_colorMap[11][0] = 0xaf;
|
||||
_colorMap[11][1] = 0xfa;
|
||||
_colorMap[11][2] = 0xaf;
|
||||
_colorMap[11][3] = 0xfa;
|
||||
|
||||
|
||||
_colorMap[12][0] = 0x77;
|
||||
_colorMap[12][1] = 0xdd;
|
||||
_colorMap[12][2] = 0x77;
|
||||
_colorMap[12][3] = 0xdd;
|
||||
|
||||
_colorMap[13][0] = 0xcc;
|
||||
_colorMap[13][1] = 0xcc;
|
||||
_colorMap[13][2] = 0xcc;
|
||||
_colorMap[13][3] = 0xcc;
|
||||
|
||||
// TODO
|
||||
_colorMap[14][0] = 0x77;
|
||||
_colorMap[14][1] = 0xdd;
|
||||
_colorMap[14][2] = 0x77;
|
||||
_colorMap[14][3] = 0xdd;
|
||||
|
||||
Graphics::Surface *surf = loadBundledImage("driller_border");
|
||||
surf->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
_border = new Graphics::ManagedSurface();
|
||||
_border->copyFrom(*surf);
|
||||
surf->free();
|
||||
delete surf;
|
||||
|
||||
file.close();
|
||||
file.open("driller.c64.title.bitmap");
|
||||
|
||||
Common::File colorFile1;
|
||||
colorFile1.open("driller.c64.title.colors1");
|
||||
Common::File colorFile2;
|
||||
colorFile2.open("driller.c64.title.colors2");
|
||||
|
||||
_title = loadAndConvertDoodleImage(&file, &colorFile1, &colorFile2, (byte *)&kC64Palette);
|
||||
} else
|
||||
error("Unknown C64 release");
|
||||
|
||||
_playerSid = new DrillerSIDPlayer();
|
||||
}
|
||||
|
||||
|
||||
void DrillerEngine::drawC64UI(Graphics::Surface *surface) {
|
||||
|
||||
uint8 r, g, b;
|
||||
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xAA, 0xAA, 0xAA);
|
||||
|
||||
Common::Rect cover;
|
||||
|
||||
uint32 color = 0;
|
||||
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
||||
color = (*_gfx->_colorRemaps)[color];
|
||||
}
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
int score = _gameStateVars[k8bitVariableScore];
|
||||
drawStringInSurface(_currentArea->_name, 200, 184, front, back, surface);
|
||||
cover = Common::Rect(150, 143, 183, 167);
|
||||
|
||||
surface->fillRect(cover, back);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 150, 148 - 4, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 150, 156 - 4, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 150, 164 - 4, front, back, surface);
|
||||
if (_playerHeightNumber >= 0)
|
||||
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 54 + 6, 164 - 3, front, back, surface);
|
||||
else
|
||||
drawStringInSurface(Common::String::format("%s", "J"), 54 + 6, 164 - 3, front, back, surface);
|
||||
|
||||
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 46, 148 - 3, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 46, 156 - 3, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%07d", score), 239, 128, front, back, surface);
|
||||
|
||||
int seconds, minutes, hours;
|
||||
getTimeFromCountdown(seconds, minutes, hours);
|
||||
drawStringInSurface(Common::String::format("%02d", hours), 207, 8, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", minutes), 230, 8, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", seconds), 254, 8, front, back, surface);
|
||||
|
||||
Common::String message;
|
||||
int deadline;
|
||||
getLatestMessages(message, deadline);
|
||||
if (deadline <= _countdown) {
|
||||
drawStringInSurface(message, 191, 176, back, front, surface);
|
||||
_temporaryMessages.push_back(message);
|
||||
_temporaryMessageDeadlines.push_back(deadline);
|
||||
} else {
|
||||
if (_currentArea->_gasPocketRadius == 0)
|
||||
message = _messagesList[2];
|
||||
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
||||
message = _messagesList[0];
|
||||
else
|
||||
message = _messagesList[1];
|
||||
|
||||
drawStringInSurface(message, 191, 176, front, back, surface);
|
||||
}
|
||||
|
||||
uint32 green = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x68, 0xa9, 0x41);
|
||||
int energy = _gameStateVars[k8bitVariableEnergy];
|
||||
int shield = _gameStateVars[k8bitVariableShield];
|
||||
|
||||
if (energy >= 0) {
|
||||
Common::Rect backBar(21, 183, 85 - energy, 190);
|
||||
surface->fillRect(backBar, back);
|
||||
Common::Rect energyBar(84 - energy, 184, 84, 190);
|
||||
surface->fillRect(energyBar, green);
|
||||
}
|
||||
|
||||
if (shield >= 0) {
|
||||
Common::Rect backBar(25 - 4, 180 - 4, 89 - shield - 4, 186 - 4);
|
||||
surface->fillRect(backBar, back);
|
||||
|
||||
Common::Rect shieldBar(88 - 4 - shield, 180 - 4, 88 - 4, 186 - 4);
|
||||
surface->fillRect(shieldBar, green);
|
||||
}
|
||||
|
||||
_gfx->readFromPalette(7, r, g, b);
|
||||
uint32 yellow = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
surface->fillRect(Common::Rect(87, 156, 104, 166), back);
|
||||
drawCompass(surface, 94, 156, _yaw - 30, 11, 75, yellow);
|
||||
surface->fillRect(Common::Rect(224, 151, 235, 160), back);
|
||||
drawCompass(surface, 223, 156, _pitch - 30, 12, 60, yellow);
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
1072
engines/freescape/games/driller/c64.music.cpp
Normal file
1072
engines/freescape/games/driller/c64.music.cpp
Normal file
File diff suppressed because it is too large
Load Diff
239
engines/freescape/games/driller/c64.music.h
Normal file
239
engines/freescape/games/driller/c64.music.h
Normal file
@@ -0,0 +1,239 @@
|
||||
/* 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 "audio/sid.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
class DrillerSIDPlayer {
|
||||
|
||||
// --- Voice State Structure ---
|
||||
struct VoiceState {
|
||||
// Pointers & Indices
|
||||
const uint8_t *trackDataPtr; // Pointer to current track data array
|
||||
uint8_t trackIndex; // Index within trackDataPtr
|
||||
const uint8_t *patternDataPtr; // Pointer to current pattern data array
|
||||
uint8_t patternIndex; // Index within patternDataPtr
|
||||
uint8_t instrumentIndex; // Current instrument (0-21, scaled by 8 for lookup)
|
||||
|
||||
// Playback Control & Tempo
|
||||
int8_t delayCounter; // Counts down frames for note duration (maps to voiceX_ctrl2)
|
||||
uint8_t noteDuration; // Duration set by FD xx (maps to voiceX_something+2)
|
||||
uint8_t gateMask; // Control gating/retriggering (maps to control3 behavior)
|
||||
|
||||
// Note & Frequency
|
||||
uint8_t currentNote; // Current raw note value (0-95)
|
||||
uint8_t portaTargetNote; // Target note for portamento
|
||||
uint16_t currentFreq; // Current frequency being sent to SID
|
||||
uint16_t baseFreq; // Note frequency without effects
|
||||
uint16_t targetFreq; // Used for portamento target
|
||||
|
||||
// Pulse Width
|
||||
uint16_t pulseWidth; // Current pulse width
|
||||
// Placeholder for PWM effects if needed later
|
||||
|
||||
// ADSR
|
||||
uint8_t attackDecay; // SID Attack / Decay register value
|
||||
uint8_t sustainRelease; // SID Sustain / Release register value
|
||||
|
||||
// Effects State
|
||||
uint8_t effect; // Current active effect (0:None, 1:Arpeggio, 2:Vibrato, 3:Portamento Up, 4:Portamento Down, 5: PWM LFO?)
|
||||
uint8_t arpeggioIndex; // Index in arpeggio table
|
||||
uint8_t arpeggioSpeed; // Counter divisor for arpeggio step
|
||||
uint8_t arpeggioCounter; // Counter for arpeggio step
|
||||
uint8_t arpeggioNoteOffsetIndex; // 0, 1, 2 for arpeggio notes
|
||||
|
||||
int16_t vibratoDepth; // Depth for vibrato effect
|
||||
uint8_t vibratoSpeed; // Speed/delay for vibrato effect
|
||||
uint8_t vibratoDelay; // Counter for vibrato step
|
||||
uint8_t vibratoDirection; // 0: up, 1: down
|
||||
int16_t vibratoCurrentOffset; // Current frequency offset for vibrato
|
||||
|
||||
int16_t portaSpeed; // Speed for portamento effect (positive for up, negative for down)
|
||||
|
||||
// Hard Restart / Buzz Effect (from L1005, possibly instrument related)
|
||||
uint8_t hardRestartValue; // Value from instrument table (a1+5)
|
||||
uint8_t hardRestartDelay; // Counter for delay phase (voiceX_whatever+3)
|
||||
uint8_t hardRestartCounter; // Counter for frequency change phase (voiceX_whatever+4)
|
||||
bool hardRestartActive; // Is the effect currently running?
|
||||
|
||||
// From disassembly variables (mapping might need refinement)
|
||||
// voice1_whatever: 0CCE[5] - effect state? (arp, vib, porta)
|
||||
uint8_t whatever0; // 0CCE - Vibrato state? (0=off, 1=active, 3-4=sweep?)
|
||||
uint8_t whatever1; // 0CCF - Arpeggio state? (0=off, 1=active)
|
||||
uint8_t whatever2; // 0CD0 - Portamento type (0=off, 1=down(FB), 2=up(FC), 3=down H(FB), 4=up H(FC))?
|
||||
uint8_t whatever3; // 0CD1 - Hard restart delay counter
|
||||
uint8_t whatever4; // 0CD2 - Hard restart step counter
|
||||
|
||||
// voice1_whatever2: 0CD4 - Vibrato direction toggle?
|
||||
uint8_t whatever2_vibDirToggle;
|
||||
|
||||
// voice1_something: 0CE3[3] - Porta speed?, Note duration
|
||||
uint16_t portaStepRaw; // 0CE3/4 - Raw value from FB/FC command
|
||||
// uint8_t noteDuration // 0CE5 - Covered above
|
||||
|
||||
// voice1_something_else: 0CE7[3] - PW Low, PW High? (ADSR in disassembly?) - Needs clarification
|
||||
uint8_t something_else[3]; // Re-add this array as it's used in the code logic
|
||||
|
||||
// voice1_ctrl0: 0CF8 - ADSR lower nibble (Sustain/Release)
|
||||
uint8_t ctrl0; // Re-add this as it's used in the code logic
|
||||
|
||||
// voice1_ctrl1: 0CF9 - Arpeggio table index / Arp speed upper nibble
|
||||
uint8_t arpTableIndex;
|
||||
uint8_t arpSpeedHiNibble;
|
||||
|
||||
// voice1_ctrl2: 0CFE - Note delay counter (covered by delayCounter)
|
||||
|
||||
// voice1_stuff: 0D14[7] - Current freq, base freq, hard restart freq store? Arp counter?
|
||||
uint16_t stuff_freq_porta_vib; // 0D14/15 - Current frequency including porta/vib
|
||||
uint16_t stuff_freq_base; // 0D16/17 - Base frequency of the note
|
||||
uint16_t stuff_freq_hard_restart; // 0D18/19 - Frequency stored during hard restart buzz
|
||||
uint8_t stuff_arp_counter; // 0D1A - Arpeggio counter (0..speed-1)
|
||||
uint8_t stuff_arp_note_index; // 0D1B - Index into arp notes (0, 1, 2)
|
||||
|
||||
// voice1_things: 0D29[7] - Vibrato state/params
|
||||
uint8_t things_vib_state; // 0D29 - Vibrato state (0=down1, 1=up, 2=down2, 3=sweepdown1, 4=sweepup)
|
||||
uint16_t things_vib_depth; // 0D2A/2B - Vibrato depth
|
||||
uint8_t things_vib_delay_reload; // 0D2C - Vibrato delay reload value
|
||||
uint8_t things_vib_delay_ctr; // 0D2D - Vibrato delay counter
|
||||
// 0D2E/F unused?
|
||||
uint8_t currentNoteSlideTarget; // 0D30 - Last played note (used for slide target?)
|
||||
|
||||
// voice1_two_ctr: 0D3E - Glide down timer? (Instrument related)
|
||||
uint8_t glideDownTimer;
|
||||
|
||||
// Temp values during processing
|
||||
uint8_t waveform; // Current waveform for SID
|
||||
bool keyOn; // Current key state (attack vs release)
|
||||
bool sync; // Sync bit state
|
||||
bool ringMod; // Ring mod bit state
|
||||
|
||||
// Pulse width parts matching something_else (if it maps to PW)
|
||||
uint8_t pwLo() const { return something_else[0]; } // Example mapping
|
||||
uint8_t pwHi() const { return something_else[2]; } // Example mapping
|
||||
void setPwLo(uint8_t val) { something_else[0] = val; }
|
||||
void setPwHi(uint8_t val) { something_else[2] = val; }
|
||||
|
||||
void reset() {
|
||||
trackDataPtr = nullptr;
|
||||
trackIndex = 0;
|
||||
patternDataPtr = nullptr;
|
||||
patternIndex = 0;
|
||||
instrumentIndex = 0;
|
||||
delayCounter = 0;
|
||||
noteDuration = 0;
|
||||
gateMask = 0xFF;
|
||||
currentNote = 0;
|
||||
portaTargetNote = 0;
|
||||
currentFreq = 0;
|
||||
baseFreq = 0;
|
||||
targetFreq = 0;
|
||||
pulseWidth = 0;
|
||||
attackDecay = 0;
|
||||
sustainRelease = 0;
|
||||
effect = 0;
|
||||
arpeggioIndex = 0;
|
||||
arpeggioSpeed = 0;
|
||||
arpeggioCounter = 0;
|
||||
arpeggioNoteOffsetIndex = 0;
|
||||
vibratoDepth = 0;
|
||||
vibratoSpeed = 0;
|
||||
vibratoDelay = 0;
|
||||
vibratoDirection = 0;
|
||||
vibratoCurrentOffset = 0;
|
||||
portaSpeed = 0;
|
||||
hardRestartValue = 0;
|
||||
hardRestartDelay = 0;
|
||||
hardRestartCounter = 0;
|
||||
hardRestartActive = false;
|
||||
waveform = 0x10; // Default to triangle?
|
||||
keyOn = false;
|
||||
sync = false;
|
||||
ringMod = false;
|
||||
|
||||
// Reset mapped vars
|
||||
whatever0 = 0;
|
||||
whatever1 = 0;
|
||||
whatever2 = 0;
|
||||
whatever3 = 0;
|
||||
whatever4 = 0;
|
||||
whatever2_vibDirToggle = 0;
|
||||
portaStepRaw = 0;
|
||||
memset(something_else, 0, sizeof(something_else)); // Reset the array
|
||||
ctrl0 = 0; // Reset the added member
|
||||
arpTableIndex = 0;
|
||||
arpSpeedHiNibble = 0;
|
||||
stuff_freq_porta_vib = 0;
|
||||
stuff_freq_base = 0;
|
||||
stuff_freq_hard_restart = 0;
|
||||
stuff_arp_counter = 0;
|
||||
stuff_arp_note_index = 0;
|
||||
things_vib_state = 0;
|
||||
things_vib_depth = 0;
|
||||
things_vib_delay_reload = 0;
|
||||
things_vib_delay_ctr = 0;
|
||||
currentNoteSlideTarget = 0;
|
||||
glideDownTimer = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Member Variables ---
|
||||
SID::SID *_sid;
|
||||
|
||||
// Player State
|
||||
enum PlayState { STOPPED,
|
||||
PLAYING,
|
||||
CHANGING_TUNE };
|
||||
PlayState _playState;
|
||||
uint8_t _targetTuneIndex; // Tune index requested via startMusic
|
||||
|
||||
// Global Timing
|
||||
uint8_t _globalTempo; // Tempo value for current tune (0xD10)
|
||||
int8_t _globalTempoCounter; // Frame counter for tempo (0xD12), signed to handle < 0 check
|
||||
uint8_t _framePhase; // Tracks which voice is being processed (0, 7, 14)
|
||||
|
||||
// Voice States
|
||||
VoiceState _voiceState[3];
|
||||
|
||||
// Internal helpers
|
||||
uint8_t _tempControl3; // Temporary storage for gate mask (0xD13)
|
||||
// uint8_t _tempControl1; // Temp storage from instrument data (0xD11)
|
||||
|
||||
public:
|
||||
DrillerSIDPlayer();
|
||||
~DrillerSIDPlayer();
|
||||
void startMusic(int tuneIndex = 1);
|
||||
void stopMusic();
|
||||
|
||||
private:
|
||||
void SID_Write(int reg, uint8_t data);
|
||||
void initSID();
|
||||
void onTimer();
|
||||
void handleChangeTune(int tuneIndex);
|
||||
void handleResetVoices();
|
||||
void playVoice(int voiceIndex);
|
||||
void applyNote(VoiceState &v, int sidOffset, const uint8_t *instA0, const uint8_t *instA1, int voiceIndex);
|
||||
void applyContinuousEffects(VoiceState &v, int sidOffset, const uint8_t *instA0, const uint8_t *instA1);
|
||||
void applyHardRestart(VoiceState &v, int sidOffset, const uint8_t *instA0, const uint8_t *instA1);
|
||||
};
|
||||
|
||||
} // namespace Freescape
|
||||
243
engines/freescape/games/driller/cpc.cpp
Normal file
243
engines/freescape/games/driller/cpc.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/* 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 "common/memstream.h"
|
||||
|
||||
#include "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
void DrillerEngine::initCPC() {
|
||||
_viewArea = Common::Rect(36, 16, 284, 117);
|
||||
}
|
||||
|
||||
byte kCPCPaletteTitleData[4][3] = {
|
||||
{0x00, 0x00, 0x00},
|
||||
{0x00, 0x80, 0xff},
|
||||
{0xff, 0x00, 0x00},
|
||||
{0xff, 0xff, 0x00},
|
||||
};
|
||||
|
||||
byte kCPCPaletteBorderData[4][3] = {
|
||||
{0x00, 0x00, 0x00},
|
||||
{0xff, 0x80, 0x00},
|
||||
{0x80, 0xff, 0xff},
|
||||
{0x00, 0x80, 0x00},
|
||||
};
|
||||
|
||||
byte getCPCPixelMode1(byte cpc_byte, int index) {
|
||||
if (index == 0)
|
||||
return ((cpc_byte & 0x08) >> 2) | ((cpc_byte & 0x80) >> 7);
|
||||
else if (index == 1)
|
||||
return ((cpc_byte & 0x04) >> 1) | ((cpc_byte & 0x40) >> 6);
|
||||
else if (index == 2)
|
||||
return (cpc_byte & 0x02) | ((cpc_byte & 0x20) >> 5);
|
||||
else if (index == 3)
|
||||
return ((cpc_byte & 0x01) << 1) | ((cpc_byte & 0x10) >> 4);
|
||||
else
|
||||
error("Invalid index %d requested", index);
|
||||
}
|
||||
|
||||
byte getCPCPixelMode0(byte cpc_byte, int index) {
|
||||
if (index == 0) {
|
||||
// Extract Pixel 0 from the byte
|
||||
return ((cpc_byte & 0x02) >> 1) | // Bit 1 -> Bit 3 (MSB)
|
||||
((cpc_byte & 0x20) >> 4) | // Bit 5 -> Bit 2
|
||||
((cpc_byte & 0x08) >> 1) | // Bit 3 -> Bit 1
|
||||
((cpc_byte & 0x80) >> 7); // Bit 7 -> Bit 0 (LSB)
|
||||
}
|
||||
else if (index == 2) {
|
||||
// Extract Pixel 1 from the byte
|
||||
return ((cpc_byte & 0x01) << 3) | // Bit 0 -> Bit 3 (MSB)
|
||||
((cpc_byte & 0x10) >> 2) | // Bit 4 -> Bit 2
|
||||
((cpc_byte & 0x04) >> 1) | // Bit 2 -> Bit 1
|
||||
((cpc_byte & 0x40) >> 6); // Bit 6 -> Bit 0 (LSB)
|
||||
}
|
||||
else {
|
||||
error("Invalid index %d requested", index);
|
||||
}
|
||||
}
|
||||
|
||||
byte getCPCPixel(byte cpc_byte, int index, bool mode1) {
|
||||
if (mode1)
|
||||
return getCPCPixelMode1(cpc_byte, index);
|
||||
else
|
||||
return getCPCPixelMode0(cpc_byte, index);
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *readCPCImage(Common::SeekableReadStream *file, bool mode1) {
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
||||
surface->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surface->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
|
||||
int x, y;
|
||||
file->seek(0x80);
|
||||
for (int block = 0; block < 8; block++) {
|
||||
for (int line = 0; line < 25; line++) {
|
||||
for (int offset = 0; offset < 320 / 4; offset++) {
|
||||
byte cpc_byte = file->readByte(); // Get CPC byte
|
||||
|
||||
// Process first pixel
|
||||
int pixel_0 = getCPCPixel(cpc_byte, 0, mode1); // %Aa
|
||||
y = line * 8 + block ; // Coord Y for the pixel
|
||||
x = 4 * offset + 0; // Coord X for the pixel
|
||||
surface->setPixel(x, y, pixel_0);
|
||||
|
||||
// Process second pixel
|
||||
y = line * 8 + block ; // Coord Y for the pixel
|
||||
x = 4 * offset + 1; // Coord X for the pixel
|
||||
if (mode1) {
|
||||
int pixel_1 = getCPCPixel(cpc_byte, 1, mode1); // %Bb
|
||||
surface->setPixel(x, y, pixel_1);
|
||||
} else
|
||||
surface->setPixel(x, y, pixel_0);
|
||||
|
||||
// Process third pixel
|
||||
int pixel_2 = getCPCPixel(cpc_byte, 2, mode1); // %Cc
|
||||
y = line * 8 + block ; // Coord Y for the pixel
|
||||
x = 4 * offset + 2; // Coord X for the pixel
|
||||
surface->setPixel(x, y, pixel_2);
|
||||
|
||||
// Process fourth pixel
|
||||
y = line * 8 + block ; // Coord Y for the pixel
|
||||
x = 4 * offset + 3; // Coord X for the pixel
|
||||
if (mode1) {
|
||||
int pixel_3 = getCPCPixel(cpc_byte, 3, mode1); // %Dd
|
||||
surface->setPixel(x, y, pixel_3);
|
||||
} else
|
||||
surface->setPixel(x, y, pixel_2);
|
||||
}
|
||||
}
|
||||
// We should skip the next 48 bytes, because they are padding the block to be 2048 bytes
|
||||
file->seek(48, SEEK_CUR);
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsCPCFullGame() {
|
||||
Common::File file;
|
||||
|
||||
file.open("DSCN1.BIN");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DSCN1.BIN");
|
||||
|
||||
_title = readCPCImage(&file, true);
|
||||
_title->setPalette((byte*)&kCPCPaletteTitleData, 0, 4);
|
||||
|
||||
file.close();
|
||||
file.open("DSCN2.BIN");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DSCN2.BIN");
|
||||
|
||||
_border = readCPCImage(&file, true);
|
||||
_border->setPalette((byte*)&kCPCPaletteBorderData, 0, 4);
|
||||
|
||||
file.close();
|
||||
file.open("DRILL.BIN");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DRILL.BIN");
|
||||
|
||||
loadMessagesFixedSize(&file, 0x214c, 14, 20);
|
||||
loadFonts(&file, 0x5b69);
|
||||
loadGlobalObjects(&file, 0x1d07, 8);
|
||||
load8bitBinary(&file, 0x5ccb, 16);
|
||||
}
|
||||
|
||||
void DrillerEngine::drawCPCUI(Graphics::Surface *surface) {
|
||||
uint32 color = _currentArea->_underFireBackgroundColor;
|
||||
uint8 r, g, b;
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
color = _currentArea->_usualBackgroundColor;
|
||||
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
||||
color = (*_gfx->_colorRemaps)[color];
|
||||
}
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
int score = _gameStateVars[k8bitVariableScore];
|
||||
drawStringInSurface(_currentArea->_name, 200, 185, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 151, 145, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 151, 153, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 151, 161, front, back, surface);
|
||||
if (_playerHeightNumber >= 0)
|
||||
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 54, 161, front, back, surface);
|
||||
else
|
||||
drawStringInSurface(Common::String::format("%s", "J"), 54, 161, front, back, surface);
|
||||
|
||||
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 47, 145, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 44, 153, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%07d", score), 239, 129, front, back, surface);
|
||||
|
||||
int seconds, minutes, hours;
|
||||
getTimeFromCountdown(seconds, minutes, hours);
|
||||
drawStringInSurface(Common::String::format("%02d", hours), 209, 8, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", minutes), 232, 8, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", seconds), 255, 8, front, back, surface);
|
||||
|
||||
Common::String message;
|
||||
int deadline;
|
||||
getLatestMessages(message, deadline);
|
||||
if (deadline <= _countdown) {
|
||||
drawStringInSurface(message, 191, 177, back, front, surface);
|
||||
_temporaryMessages.push_back(message);
|
||||
_temporaryMessageDeadlines.push_back(deadline);
|
||||
} else if (_messagesList.size() > 0) {
|
||||
if (_currentArea->_gasPocketRadius == 0)
|
||||
message = _messagesList[2];
|
||||
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
||||
message = _messagesList[0];
|
||||
else
|
||||
message = _messagesList[1];
|
||||
|
||||
drawStringInSurface(message, 191, 177, front, back, surface);
|
||||
}
|
||||
|
||||
int energy = _gameStateVars[k8bitVariableEnergy];
|
||||
int shield = _gameStateVars[k8bitVariableShield];
|
||||
|
||||
if (energy >= 0) {
|
||||
Common::Rect backBar(25, 184, 89 - energy, 191);
|
||||
surface->fillRect(backBar, back);
|
||||
Common::Rect energyBar(88 - energy, 184, 88, 191);
|
||||
surface->fillRect(energyBar, front);
|
||||
}
|
||||
|
||||
if (shield >= 0) {
|
||||
Common::Rect backBar(25, 177, 89 - shield, 183);
|
||||
surface->fillRect(backBar, back);
|
||||
|
||||
Common::Rect shieldBar(88 - shield, 177, 88, 183);
|
||||
surface->fillRect(shieldBar, front);
|
||||
}
|
||||
|
||||
drawCompass(surface, 87, 156, _yaw - 30, 10, 75, front);
|
||||
drawCompass(surface, 230, 156, _pitch - 30, 10, 60, front);
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
457
engines/freescape/games/driller/dos.cpp
Normal file
457
engines/freescape/games/driller/dos.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/* 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 "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
extern byte kEGADefaultPalette[16][3];
|
||||
extern byte kCGAPaletteRedGreen[4][3];
|
||||
extern byte kCGAPalettePinkBlue[4][3];
|
||||
extern byte kHerculesPaletteGreen[2][3];
|
||||
|
||||
void DrillerEngine::initDOS() {
|
||||
if (_renderMode == Common::kRenderEGA)
|
||||
_viewArea = Common::Rect(40, 16, 280, 117);
|
||||
else if (_renderMode == Common::kRenderHercG)
|
||||
_viewArea = Common::Rect(112, 64, 607, 224);
|
||||
else if (_renderMode == Common::kRenderCGA)
|
||||
_viewArea = Common::Rect(36, 16, 284, 117);
|
||||
else
|
||||
error("Invalid or unknown render mode");
|
||||
|
||||
_moveFowardArea = Common::Rect(73, 144, 101, 152);
|
||||
_moveLeftArea = Common::Rect(73, 150, 86, 159);
|
||||
_moveRightArea = Common::Rect(88, 152, 104, 160);
|
||||
_moveBackArea = Common::Rect(73, 160, 101, 168);
|
||||
_moveUpArea = Common::Rect(219, 144, 243, 155);
|
||||
_moveDownArea = Common::Rect(219, 157, 243, 167);
|
||||
_deployDrillArea = Common::Rect(140, 175, 179, 191);
|
||||
_infoScreenArea = Common::Rect(130, 125, 188, 144);
|
||||
|
||||
_soundIndexShoot = 1;
|
||||
_soundIndexCollide = 2;
|
||||
_soundIndexStepDown = 3;
|
||||
_soundIndexStepUp = 4;
|
||||
_soundIndexMenu = 2;
|
||||
_soundIndexStart = 9;
|
||||
_soundIndexAreaChange = 5;
|
||||
_soundIndexHit = 2;
|
||||
|
||||
_soundIndexFall = 14;
|
||||
_soundIndexNoShield = 20;
|
||||
_soundIndexNoEnergy = 20;
|
||||
_soundIndexFallen = 20;
|
||||
_soundIndexTimeout = 20;
|
||||
_soundIndexForceEndGame = 20;
|
||||
_soundIndexCrushed = 20;
|
||||
}
|
||||
|
||||
/*
|
||||
The following functions are only used for decoding title images for
|
||||
the US release of Driller ("Space Station Oblivion")
|
||||
*/
|
||||
|
||||
uint32 DrillerEngine::getPixel8bitTitleImage(int index) {
|
||||
if (index < 4 || _renderMode == Common::kRenderEGA) {
|
||||
return index;
|
||||
}
|
||||
return index / 4;
|
||||
}
|
||||
|
||||
void DrillerEngine::renderPixels8bitTitleImage(Graphics::ManagedSurface *surface, int &x, int &y, int pixels) {
|
||||
int c1 = pixels >> 4;
|
||||
int c2 = pixels & 0xf;
|
||||
|
||||
if (x == 320) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_renderMode == Common::kRenderCGA) {
|
||||
surface->setPixel(x, y, getPixel8bitTitleImage(c1 / 4));
|
||||
x++;
|
||||
if (x == 320) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
surface->setPixel(x, y, getPixel8bitTitleImage(c1));
|
||||
x++;
|
||||
|
||||
if (x == 320) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_renderMode == Common::kRenderCGA) {
|
||||
surface->setPixel(x, y, getPixel8bitTitleImage(c2 / 4));
|
||||
x++;
|
||||
|
||||
if (x == 320) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
surface->setPixel(x, y, getPixel8bitTitleImage(c2));
|
||||
x++;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *DrillerEngine::load8bitTitleImage(Common::SeekableReadStream *file, int offset) {
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
||||
surface->create(_screenW, _screenH, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surface->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
|
||||
file->seek(offset);
|
||||
for (int y = 0; y < 200; ++y) {
|
||||
if (file->eos ()) break;
|
||||
|
||||
// Start of line data (0x02) or [premature] end of data (0x00)
|
||||
int sol = file->readByte();
|
||||
if (sol == 0) break;
|
||||
assert(sol == 2);
|
||||
|
||||
int x = 0;
|
||||
while (x < 320) {
|
||||
int command = file->readByte();
|
||||
if (command & 0x80) {
|
||||
// Copy 2*N bytes verbatim
|
||||
int repeat = (257 - command) * 2;
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
int pixels = file->readByte();
|
||||
renderPixels8bitTitleImage(surface, x, y, pixels);
|
||||
}
|
||||
} else {
|
||||
// Repeat 2 bytes of the input N times
|
||||
int repeat = command + 1;
|
||||
int pixels1 = file->readByte();
|
||||
int pixels2 = file->readByte();
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
renderPixels8bitTitleImage(surface, x, y, pixels1);
|
||||
renderPixels8bitTitleImage(surface, x, y, pixels2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
byte kCGAPalettePinkBlueWhiteData[4][3] = {
|
||||
{0x00, 0x00, 0x00},
|
||||
{0x55, 0xff, 0xff},
|
||||
{0xff, 0x55, 0xff},
|
||||
{0xff, 0xff, 0xff},
|
||||
};
|
||||
|
||||
/*
|
||||
The following function is only used for decoding images for
|
||||
the Driller DOS demo
|
||||
*/
|
||||
|
||||
Graphics::ManagedSurface *DrillerEngine::load8bitDemoImage(Common::SeekableReadStream *file, int offset) {
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
||||
surface->create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surface->fillRect(Common::Rect(0, 0, 320, 200), 0);
|
||||
file->seek(offset);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (true) {
|
||||
byte pixels = file->readByte();
|
||||
for (int b = 0; b < 4; b++) {
|
||||
int color = pixels & 3;
|
||||
pixels = pixels >> 2;
|
||||
surface->setPixel(i + (3 - b), 2 * j, color);
|
||||
}
|
||||
i = i + 4;
|
||||
if (i == 320) {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
if (j == 100)
|
||||
break;
|
||||
}
|
||||
file->seek(0xc0, SEEK_CUR);
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (true) {
|
||||
byte pixels = file->readByte();
|
||||
for (int b = 0; b < 4; b++) {
|
||||
int color = pixels & 3;
|
||||
pixels = pixels >> 2;
|
||||
surface->setPixel(i + (3 - b), 2 * j + 1, color);
|
||||
}
|
||||
i = i + 4;
|
||||
if (i == 320) {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
if (j == 100)
|
||||
break;
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsDOSFullGame() {
|
||||
Common::File file;
|
||||
if (_renderMode == Common::kRenderEGA) {
|
||||
file.open("SCN1E.DAT");
|
||||
if (file.isOpen()) {
|
||||
_title = load8bitBinImage(&file, 0x0);
|
||||
_title->setPalette((byte*)&kEGADefaultPalette, 0, 16);
|
||||
}
|
||||
file.close();
|
||||
file.open("EGATITLE.RL");
|
||||
if (file.isOpen()) {
|
||||
_title = load8bitTitleImage(&file, 0x1b2);
|
||||
_title->setPalette((byte*)&kEGADefaultPalette, 0, 16);
|
||||
}
|
||||
file.close();
|
||||
|
||||
file.open("DRILLE.EXE");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DRILLE.EXE");
|
||||
|
||||
loadSpeakerFxDOS(&file, 0x4397 + 0x200, 0x4324 + 0x200, 20);
|
||||
loadMessagesFixedSize(&file, 0x4135, 14, 20);
|
||||
loadFonts(&file, 0x99dd);
|
||||
loadGlobalObjects(&file, 0x3b42, 8);
|
||||
load8bitBinary(&file, 0x9b40, 16);
|
||||
_border = load8bitBinImage(&file, 0x210);
|
||||
_border->setPalette((byte*)&kEGADefaultPalette, 0, 16);
|
||||
} else if (_renderMode == Common::kRenderCGA) {
|
||||
file.open("SCN1C.DAT");
|
||||
if (file.isOpen()) {
|
||||
_title = load8bitBinImage(&file, 0x0);
|
||||
_title->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
|
||||
}
|
||||
file.close();
|
||||
file.open("CGATITLE.RL");
|
||||
if (file.isOpen()) {
|
||||
_title = load8bitTitleImage(&file, 0x1b2);
|
||||
_title->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
|
||||
}
|
||||
file.close();
|
||||
file.open("DRILLC.EXE");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DRILLC.EXE");
|
||||
|
||||
loadSpeakerFxDOS(&file, 0x27e7 + 0x200, 0x2774 + 0x200, 20);
|
||||
|
||||
loadFonts(&file, 0x07a4a);
|
||||
loadMessagesFixedSize(&file, 0x2585, 14, 20);
|
||||
loadGlobalObjects(&file, 0x1fa2, 8);
|
||||
load8bitBinary(&file, 0x7bb0, 4);
|
||||
_border = load8bitBinImage(&file, 0x210);
|
||||
_border->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
|
||||
swapPalette(1);
|
||||
} else if (_renderMode == Common::kRenderHercG) {
|
||||
file.open("SCN1H.DAT");
|
||||
if (file.isOpen()) {
|
||||
_title = load8bitBinImage(&file, 0x0);
|
||||
_title->setPalette((byte*)&kHerculesPaletteGreen, 0, 2);
|
||||
}
|
||||
file.close();
|
||||
file.open("DRILLH.EXE");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open DRILLH.EXE");
|
||||
|
||||
//loadSpeakerFxDOS(&file, 0x27e7 + 0x200, 0x2774 + 0x200, 20);
|
||||
|
||||
loadFonts(&file, 0x8871);
|
||||
loadMessagesFixedSize(&file, 0x3411, 14, 20);
|
||||
load8bitBinary(&file, 0x89e0, 4);
|
||||
loadGlobalObjects(&file, 0x2d02, 8);
|
||||
_border = load8bitBinImage(&file, 0x210);
|
||||
_border->setPalette((byte*)&kHerculesPaletteGreen, 0, 2);
|
||||
} else
|
||||
error("Unsupported video mode for DOS");
|
||||
|
||||
if (_renderMode != Common::kRenderHercG) {
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator"));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator"));
|
||||
|
||||
_indicators[0]->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
_indicators[1]->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsDOSDemo() {
|
||||
Common::File file;
|
||||
_renderMode = Common::kRenderCGA; // DOS demos is CGA only
|
||||
_viewArea = Common::Rect(36, 16, 284, 117); // correct view area
|
||||
_gfx->_renderMode = _renderMode;
|
||||
file.open("d1");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'd1' file");
|
||||
|
||||
_title = load8bitDemoImage(&file, 0x0);
|
||||
_title->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
|
||||
|
||||
file.close();
|
||||
file.open("d2");
|
||||
if (!file.isOpen())
|
||||
error("Failed to open 'd2' file");
|
||||
|
||||
loadFonts(&file, 0x4eb0);
|
||||
loadMessagesFixedSize(&file, 0x636, 14, 20);
|
||||
loadGlobalObjects(&file, 0x53, 8);
|
||||
load8bitBinary(&file, 0x55b0, 4);
|
||||
_border = load8bitDemoImage(&file, 0x6220);
|
||||
_border->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
|
||||
|
||||
// Fixes corrupted area names in the demo data
|
||||
_areaMap[2]->_name = "LAPIS LAZULI";
|
||||
_areaMap[3]->_name = "EMERALD";
|
||||
_areaMap[8]->_name = "TOPAZ";
|
||||
file.close();
|
||||
|
||||
_indicators.push_back(loadBundledImage("driller_tank_indicator"));
|
||||
_indicators.push_back(loadBundledImage("driller_ship_indicator"));
|
||||
|
||||
_indicators[0]->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
_indicators[1]->convertToInPlace(_gfx->_texturePixelFormat);
|
||||
}
|
||||
|
||||
void DrillerEngine::drawDOSUI(Graphics::Surface *surface) {
|
||||
uint32 color = _renderMode == Common::kRenderCGA || _renderMode == Common::kRenderHercG ? 1 : 14;
|
||||
uint8 r, g, b;
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
color = _currentArea->_usualBackgroundColor;
|
||||
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
||||
color = (*_gfx->_colorRemaps)[color];
|
||||
}
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
int score = _gameStateVars[k8bitVariableScore];
|
||||
Common::Point currentAreaPos = _renderMode == Common::kRenderHercG ? Common::Point(437, 293) : Common::Point(197, 185);
|
||||
drawStringInSurface(_currentArea->_name, currentAreaPos.x, currentAreaPos.y, front, back, surface);
|
||||
|
||||
Common::Point coordinateXPos = _renderMode == Common::kRenderHercG ? Common::Point(345, 253) : Common::Point(151, 145);
|
||||
Common::Point coordinateYPos = _renderMode == Common::kRenderHercG ? Common::Point(345, 261) : Common::Point(151, 153);
|
||||
Common::Point coordinateZPos = _renderMode == Common::kRenderHercG ? Common::Point(345, 269) : Common::Point(151, 161);
|
||||
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), coordinateXPos.x, coordinateXPos.y, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), coordinateYPos.x, coordinateYPos.y, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), coordinateZPos.x, coordinateZPos.y, front, back, surface);
|
||||
|
||||
Common::Point playerHeightPos = _renderMode == Common::kRenderHercG ? Common::Point(157, 269) : Common::Point(57, 161);
|
||||
if (_playerHeightNumber >= 0)
|
||||
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), playerHeightPos.x, playerHeightPos.y, front, back, surface);
|
||||
else
|
||||
drawStringInSurface(Common::String::format("%s", "J"), playerHeightPos.x, playerHeightPos.y, front, back, surface);
|
||||
|
||||
Common::Point anglePos = _renderMode == Common::kRenderHercG ? Common::Point(141, 253) : Common::Point(47, 145);
|
||||
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), anglePos.x, anglePos.y, front, back, surface);
|
||||
|
||||
Common::Point playerStepsPos;
|
||||
|
||||
if (_renderMode == Common::kRenderHercG)
|
||||
playerStepsPos = Common::Point(130, 261);
|
||||
else if (_renderMode == Common::kRenderCGA)
|
||||
playerStepsPos = Common::Point(44, 153);
|
||||
else
|
||||
playerStepsPos = Common::Point(47, 153);
|
||||
|
||||
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), playerStepsPos.x, playerStepsPos.y, front, back, surface);
|
||||
|
||||
Common::Point scorePos = _renderMode == Common::kRenderHercG ? Common::Point(522, 237) : Common::Point(239, 129);
|
||||
drawStringInSurface(Common::String::format("%07d", score), scorePos.x, scorePos.y, front, back, surface);
|
||||
|
||||
int seconds, minutes, hours;
|
||||
getTimeFromCountdown(seconds, minutes, hours);
|
||||
|
||||
Common::Point hoursPos = _renderMode == Common::kRenderHercG ? Common::Point(462, 56) : Common::Point(208, 8);
|
||||
drawStringInSurface(Common::String::format("%02d", hours), hoursPos.x, hoursPos.y, front, back, surface);
|
||||
|
||||
Common::Point minutesPos = _renderMode == Common::kRenderHercG ? Common::Point(506, 56) : Common::Point(231, 8);
|
||||
drawStringInSurface(Common::String::format("%02d", minutes), minutesPos.x, minutesPos.y, front, back, surface);
|
||||
|
||||
Common::Point secondsPos = _renderMode == Common::kRenderHercG ? Common::Point(554, 56) : Common::Point(255, 8);
|
||||
drawStringInSurface(Common::String::format("%02d", seconds), secondsPos.x, secondsPos.y, front, back, surface);
|
||||
|
||||
Common::String message;
|
||||
int deadline;
|
||||
getLatestMessages(message, deadline);
|
||||
Common::Point messagePos = _renderMode == Common::kRenderHercG ? Common::Point(424, 285) : Common::Point(191, 177);
|
||||
if (deadline <= _countdown) {
|
||||
drawStringInSurface(message, messagePos.x, messagePos.y, back, front, surface);
|
||||
_temporaryMessages.push_back(message);
|
||||
_temporaryMessageDeadlines.push_back(deadline);
|
||||
} else {
|
||||
if (_currentArea->_gasPocketRadius == 0)
|
||||
message = _messagesList[2];
|
||||
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
||||
message = _messagesList[0];
|
||||
else
|
||||
message = _messagesList[1];
|
||||
|
||||
drawStringInSurface(message, messagePos.x, messagePos.y, front, back, surface);
|
||||
}
|
||||
|
||||
int energy = _gameStateVars[k8bitVariableEnergy];
|
||||
int shield = _gameStateVars[k8bitVariableShield];
|
||||
|
||||
if (_renderMode != Common::kRenderHercG) {
|
||||
if (energy >= 0) {
|
||||
Common::Rect backBar(20, 185, 88 - energy, 191);
|
||||
surface->fillRect(backBar, back);
|
||||
Common::Rect energyBar(87 - energy, 185, 88, 191);
|
||||
surface->fillRect(energyBar, front);
|
||||
}
|
||||
|
||||
if (shield >= 0) {
|
||||
Common::Rect backBar(20, 177, 88 - shield, 183);
|
||||
surface->fillRect(backBar, back);
|
||||
|
||||
Common::Rect shieldBar(87 - shield, 177, 88, 183);
|
||||
surface->fillRect(shieldBar, front);
|
||||
}
|
||||
}
|
||||
|
||||
if (_indicators.size() >= 2) {
|
||||
if (!_flyMode)
|
||||
surface->copyRectToSurface(*_indicators[0], 132, 127, Common::Rect(_indicators[0]->w, _indicators[0]->h));
|
||||
else
|
||||
surface->copyRectToSurface(*_indicators[1], 132, 127, Common::Rect(_indicators[1]->w, _indicators[1]->h));
|
||||
}
|
||||
|
||||
color = _renderMode == Common::kRenderHercG ? 1 : 2;
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 other = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
Common::Point compassYawPos = _renderMode == Common::kRenderHercG ? Common::Point(214, 264) : Common::Point(87, 156);
|
||||
drawCompass(surface, compassYawPos.x, compassYawPos.y, _yaw - 30, 10, 75, other);
|
||||
Common::Point compassPitchPos = _renderMode == Common::kRenderHercG ? Common::Point(502, 264) : Common::Point(230, 156);
|
||||
drawCompass(surface, compassPitchPos.x, compassPitchPos.y, _pitch - 30, 10, 60, other);
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
1054
engines/freescape/games/driller/driller.cpp
Normal file
1054
engines/freescape/games/driller/driller.cpp
Normal file
File diff suppressed because it is too large
Load Diff
134
engines/freescape/games/driller/driller.h
Normal file
134
engines/freescape/games/driller/driller.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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 "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "engines/freescape/games/driller/c64.music.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
enum DrillerFontSize {
|
||||
kDrillerFontSmall,
|
||||
kDrillerFontNormal,
|
||||
};
|
||||
|
||||
class DrillerEngine : public FreescapeEngine {
|
||||
public:
|
||||
DrillerEngine(OSystem *syst, const ADGameDescription *gd);
|
||||
~DrillerEngine();
|
||||
|
||||
uint32 _initialJetEnergy;
|
||||
uint32 _initialJetShield;
|
||||
|
||||
uint32 _initialTankEnergy;
|
||||
uint32 _initialTankShield;
|
||||
|
||||
bool _useAutomaticDrilling;
|
||||
|
||||
DrillerSIDPlayer *_playerSid;
|
||||
|
||||
// Only used for Amiga and Atari ST
|
||||
Font _fontSmall;
|
||||
void drawString(const DrillerFontSize size, const Common::String &str, int x, int y, uint32 primaryColor, uint32 secondaryColor, uint32 backColor, Graphics::Surface *surface);
|
||||
|
||||
Common::HashMap<uint16, uint32> _drillStatusByArea;
|
||||
Common::HashMap<uint16, uint32> _drillMaxScoreByArea;
|
||||
Common::HashMap<uint16, uint32> _drillSuccessByArea;
|
||||
|
||||
void initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target) override;
|
||||
void initGameState() override;
|
||||
bool checkIfGameEnded() override;
|
||||
void endGame() override;
|
||||
|
||||
void gotoArea(uint16 areaID, int entranceID) override;
|
||||
|
||||
void drawInfoMenu() override;
|
||||
void drawSensorShoot(Sensor *sensor) override;
|
||||
void drawCompass(Graphics::Surface *surface, int x, int y, double degrees, double magnitude, double fov, uint32 color);
|
||||
|
||||
void pressedKey(const int keycode) override;
|
||||
Common::Error saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave = false) override;
|
||||
Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
|
||||
|
||||
private:
|
||||
bool drillDeployed(Area *area);
|
||||
GeometricObject *_drillBase;
|
||||
Math::Vector3d drillPosition();
|
||||
void addDrill(const Math::Vector3d position, bool gasFound);
|
||||
bool checkDrill(const Math::Vector3d position);
|
||||
void removeDrill(Area *area);
|
||||
void addSkanner(Area *area);
|
||||
|
||||
void loadAssets() override;
|
||||
void loadAssetsAtariFullGame() override;
|
||||
void loadAssetsAtariDemo() override;
|
||||
void loadAssetsAmigaFullGame() override;
|
||||
void loadAssetsAmigaDemo() override;
|
||||
void loadAssetsDOSFullGame() override;
|
||||
void loadAssetsDOSDemo() override;
|
||||
void loadAssetsZXFullGame() override;
|
||||
void loadAssetsCPCFullGame() override;
|
||||
void loadAssetsC64FullGame() override;
|
||||
|
||||
void drawDOSUI(Graphics::Surface *surface) override;
|
||||
void drawZXUI(Graphics::Surface *surface) override;
|
||||
void drawCPCUI(Graphics::Surface *surface) override;
|
||||
void drawC64UI(Graphics::Surface *surface) override;
|
||||
void drawAmigaAtariSTUI(Graphics::Surface *surface) override;
|
||||
bool onScreenControls(Common::Point mouse) override;
|
||||
void initAmigaAtari();
|
||||
void initDOS();
|
||||
void initZX();
|
||||
void initCPC();
|
||||
void initC64();
|
||||
|
||||
void updateTimeVariables() override;
|
||||
|
||||
Common::Rect _moveFowardArea;
|
||||
Common::Rect _moveLeftArea;
|
||||
Common::Rect _moveRightArea;
|
||||
Common::Rect _moveBackArea;
|
||||
Common::Rect _moveUpArea;
|
||||
Common::Rect _moveDownArea;
|
||||
Common::Rect _deployDrillArea;
|
||||
Common::Rect _infoScreenArea;
|
||||
Common::Rect _saveGameArea;
|
||||
Common::Rect _loadGameArea;
|
||||
|
||||
Graphics::ManagedSurface *load8bitTitleImage(Common::SeekableReadStream *file, int offset);
|
||||
Graphics::ManagedSurface *load8bitDemoImage(Common::SeekableReadStream *file, int offset);
|
||||
|
||||
uint32 getPixel8bitTitleImage(int index);
|
||||
void renderPixels8bitTitleImage(Graphics::ManagedSurface *surface, int &i, int &j, int pixels);
|
||||
Graphics::ManagedSurface *_borderExtra;
|
||||
Texture *_borderExtraTexture;
|
||||
|
||||
Common::SeekableReadStream *decryptFileAtari(const Common::Path &filename);
|
||||
Common::SeekableReadStream *decryptFileAtariVirtualWorlds(const Common::Path &filename);
|
||||
};
|
||||
|
||||
enum DrillerReleaseFlags {
|
||||
GF_AMIGA_MAGAZINE_DEMO = (1 << 0),
|
||||
GF_ATARI_MAGAZINE_DEMO = (1 << 1),
|
||||
};
|
||||
|
||||
}
|
||||
160
engines/freescape/games/driller/zx.cpp
Normal file
160
engines/freescape/games/driller/zx.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/* 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 "freescape/freescape.h"
|
||||
#include "freescape/games/driller/driller.h"
|
||||
#include "freescape/language/8bitDetokeniser.h"
|
||||
|
||||
namespace Freescape {
|
||||
|
||||
void DrillerEngine::initZX() {
|
||||
_viewArea = Common::Rect(56, 20, 264, 124);
|
||||
_soundIndexAreaChange = 10;
|
||||
}
|
||||
|
||||
void DrillerEngine::loadAssetsZXFullGame() {
|
||||
Common::File file;
|
||||
file.open("driller.zx.title");
|
||||
if (file.isOpen()) {
|
||||
_title = loadAndConvertScrImage(&file);
|
||||
} else
|
||||
error("Unable to find driller.zx.title");
|
||||
|
||||
file.close();
|
||||
|
||||
file.open("driller.zx.border");
|
||||
if (file.isOpen()) {
|
||||
_border = loadAndConvertScrImage(&file);
|
||||
} else
|
||||
error("Unable to find driller.zx.border");
|
||||
file.close();
|
||||
|
||||
file.open("driller.zx.data");
|
||||
|
||||
if (!file.isOpen())
|
||||
error("Failed to open driller.zx.data");
|
||||
|
||||
if (_variant & GF_ZX_DISC)
|
||||
loadMessagesFixedSize(&file, 0x2164, 14, 20);
|
||||
else
|
||||
loadMessagesFixedSize(&file, 0x20e4, 14, 20);
|
||||
|
||||
if (_variant & GF_ZX_RETAIL)
|
||||
loadFonts(&file, 0x62ca);
|
||||
else if (_variant & GF_ZX_BUDGET)
|
||||
loadFonts(&file, 0x5aa8);
|
||||
else if (_variant & GF_ZX_DISC)
|
||||
loadFonts(&file, 0x63f0);
|
||||
|
||||
if (_variant & GF_ZX_DISC)
|
||||
loadGlobalObjects(&file, 0x1d13, 8);
|
||||
else
|
||||
loadGlobalObjects(&file, 0x1c93, 8);
|
||||
|
||||
if (_variant & GF_ZX_RETAIL)
|
||||
load8bitBinary(&file, 0x642c, 4);
|
||||
else if (_variant & GF_ZX_BUDGET)
|
||||
load8bitBinary(&file, 0x5c0a, 4);
|
||||
else if (_variant & GF_ZX_DISC)
|
||||
load8bitBinary(&file, 0x6552, 4);
|
||||
|
||||
else
|
||||
error("Unknown ZX spectrum variant");
|
||||
}
|
||||
|
||||
void DrillerEngine::drawZXUI(Graphics::Surface *surface) {
|
||||
uint32 color = 5;
|
||||
uint8 r, g, b;
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
|
||||
color = _currentArea->_usualBackgroundColor;
|
||||
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
||||
color = (*_gfx->_colorRemaps)[color];
|
||||
}
|
||||
|
||||
_gfx->readFromPalette(color, r, g, b);
|
||||
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
||||
uint32 white = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
int score = _gameStateVars[k8bitVariableScore];
|
||||
drawStringInSurface(_currentArea->_name, 174, 188, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 151, 149, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 151, 157, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 151, 165, front, back, surface);
|
||||
if (_playerHeightNumber >= 0)
|
||||
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 72, 165, front, back, surface);
|
||||
else
|
||||
drawStringInSurface(Common::String::format("%s", "J"), 72, 165, front, back, surface);
|
||||
|
||||
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 63, 149, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 63, 157, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%07d", score), 215, 133, white, back, surface);
|
||||
|
||||
int seconds, minutes, hours;
|
||||
getTimeFromCountdown(seconds, minutes, hours);
|
||||
drawStringInSurface(Common::String::format("%02d", hours), 185, 12, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", minutes), 207, 12, front, back, surface);
|
||||
drawStringInSurface(Common::String::format("%02d", seconds), 231, 12, front, back, surface);
|
||||
|
||||
Common::String message;
|
||||
int deadline;
|
||||
getLatestMessages(message, deadline);
|
||||
if (deadline <= _countdown) {
|
||||
drawStringInSurface(message, 168, 181, back, front, surface);
|
||||
_temporaryMessages.push_back(message);
|
||||
_temporaryMessageDeadlines.push_back(deadline);
|
||||
} else {
|
||||
if (_currentArea->_gasPocketRadius == 0)
|
||||
message = _messagesList[2];
|
||||
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
||||
message = _messagesList[0];
|
||||
else
|
||||
message = _messagesList[1];
|
||||
|
||||
drawStringInSurface(message, 168, 181, front, back, surface);
|
||||
}
|
||||
|
||||
int energy = _gameStateVars[k8bitVariableEnergy];
|
||||
int shield = _gameStateVars[k8bitVariableShield];
|
||||
|
||||
if (energy >= 0) {
|
||||
Common::Rect backBar(43, 188, 107 - energy, 194);
|
||||
surface->fillRect(backBar, back);
|
||||
Common::Rect energyBar(106 - energy, 188, 106, 194);
|
||||
surface->fillRect(energyBar, front);
|
||||
}
|
||||
|
||||
if (shield >= 0) {
|
||||
Common::Rect backBar(43, 181, 107 - shield, 187);
|
||||
surface->fillRect(backBar, back);
|
||||
|
||||
Common::Rect shieldBar(106 - shield, 181, 106, 187);
|
||||
surface->fillRect(shieldBar, front);
|
||||
}
|
||||
|
||||
drawCompass(surface, 103, 160, _yaw - 30, 10, 75, front);
|
||||
drawCompass(surface, 220 - 3, 160, _pitch - 30, 10, 60, front);
|
||||
}
|
||||
|
||||
} // End of namespace Freescape
|
||||
Reference in New Issue
Block a user