Initial commit

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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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),
};
}

View 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