[Scummvm-git-logs] scummvm master -> 56000821acf65621319f30f4ca42f5295661fc62
neuromancer
noreply at scummvm.org
Tue Sep 8 20:14:04 UTC 2026
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
d8ec6445fe FREESCAPE: moved decompression of castle for c64 to unp64 in common
6efdeb5b6f FREESCAPE: decompress some freescape games using unp64
57c0ce99a6 FREESCAPE: decompress driller c64 releases using unp64
56000821ac COLONY: various forklift behavior fixes
Commit: d8ec6445fedfbf0389e92608b828c19583706e87
https://github.com/scummvm/scummvm/commit/d8ec6445fedfbf0389e92608b828c19583706e87
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-08T22:11:14+02:00
Commit Message:
FREESCAPE: moved decompression of castle for c64 to unp64 in common
Changed paths:
A common/compression/unp64/scanners/action_replay.cpp
common/compression/module.mk
common/compression/unp64/scanners/scanners.cpp
common/compression/unp64/unp64.cpp
engines/freescape/configure.engine
engines/freescape/games/castle/c64.cpp
diff --git a/common/compression/module.mk b/common/compression/module.mk
index 8ef332a20ee..4022ea0da76 100644
--- a/common/compression/module.mk
+++ b/common/compression/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS += \
unp64/exo_util.o \
unp64/scanners/scanners.o \
unp64/scanners/action_packer.o \
+ unp64/scanners/action_replay.o \
unp64/scanners/byte_boiler.o \
unp64/scanners/caution.o \
unp64/scanners/ccs.o \
diff --git a/common/compression/unp64/scanners/action_replay.cpp b/common/compression/unp64/scanners/action_replay.cpp
new file mode 100644
index 00000000000..4d5b5cef9c7
--- /dev/null
+++ b/common/compression/unp64/scanners/action_replay.cpp
@@ -0,0 +1,75 @@
+/* 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/endian.h"
+#include "common/compression/unp64/exo_util.h"
+#include "common/compression/unp64/unp64.h"
+
+namespace Common {
+namespace Unp64 {
+
+// Adapted from UNP64 2.42's Action Replay, Super Snapshot and Freeze Machine scanner.
+void scnActionReplay(UnpStr *unp) {
+ if (unp->_idFlag || unp->_depAdr)
+ return;
+
+ const byte *mem = unp->_mem;
+ if (READ_LE_UINT32(mem + 0x80f) == 0xdd0d8d7f &&
+ READ_LE_UINT32(mem + 0x927) == 0x4ce7d0ca) {
+ uint16 destination = READ_LE_UINT16(mem + 0x92b);
+ for (uint offset = 0; offset < 0x100; ++offset) {
+ const byte *code = mem + 0xa00 + offset;
+ if (code[0] == 0x99 && READ_LE_UINT16(code + 1) == destination &&
+ (code[0x11] == 0x40 || code[0x10] == 0x40)) {
+ unp->_depAdr = 0x100 + offset;
+ break;
+ }
+ }
+ if (!unp->_depAdr)
+ unp->_depAdr = destination;
+ } else if (READ_LE_UINT32(mem + 0x812) == 0xdd0d8ddc) {
+ for (uint address = 0x900; address < 0xcfff; ++address) {
+ if (READ_LE_UINT32(mem + address) == 0xa9dc0e8d &&
+ READ_LE_UINT32(mem + address + 0x0a) == 0xa9dd0e8d &&
+ mem[address + 0x18] == 0x4c) {
+ unp->_depAdr = READ_LE_UINT16(mem + address + 0x19);
+ break;
+ }
+ }
+ } else if (READ_LE_UINT32(mem + 0x80f) == 0xdd0d8d7f &&
+ READ_LE_UINT32(mem + 0x8ef) == 0x0330bd01 &&
+ READ_LE_UINT32(mem + 0x8fa) == 0x7e4c00a0 &&
+ READ_LE_UINT32(mem + 0xbc8) == 0x4c01c6df) {
+ unp->_depAdr = READ_LE_UINT16(mem + 0xbcc);
+ unp->_strMem = 2;
+ unp->_endAdr = 0x10000;
+ }
+
+ if (unp->_depAdr) {
+ unp->_rtiFrc = 1;
+ if (unp->_info->_run == -1)
+ unp->_forced = 0x80d;
+ unp->_idFlag = 1;
+ }
+}
+
+} // End of namespace Unp64
+} // End of namespace Common
diff --git a/common/compression/unp64/scanners/scanners.cpp b/common/compression/unp64/scanners/scanners.cpp
index cdd537bd9cd..30af41dcca2 100644
--- a/common/compression/unp64/scanners/scanners.cpp
+++ b/common/compression/unp64/scanners/scanners.cpp
@@ -27,6 +27,7 @@ namespace Unp64 {
void scnECA(UnpStr *unp);
void scnExpert(UnpStr *unp);
+void scnActionReplay(UnpStr *unp);
void scnCruel(UnpStr *unp);
void scnPuCrunch(UnpStr *unp);
void scnByteBoiler(UnpStr *unp);
@@ -44,6 +45,7 @@ void scnExomizer(UnpStr *unp);
Scnptr g_scanFunc[] = {
scnECA,
scnExpert,
+ scnActionReplay,
scnCruel,
scnPuCrunch,
scnByteBoiler,
diff --git a/common/compression/unp64/unp64.cpp b/common/compression/unp64/unp64.cpp
index 140bdc7e6d6..11477446b9b 100644
--- a/common/compression/unp64/unp64.cpp
+++ b/common/compression/unp64/unp64.cpp
@@ -96,6 +96,9 @@ static int isBasicRun2(int pc) {
}
int unp64(const byte *compressed, uint32 length, byte *destinationBuffer, uint32 *finalLength, const char *switches) {
+ if (!compressed || !destinationBuffer || !finalLength || length < 2)
+ return 0;
+ *finalLength = 0;
char settings[4][64];
int numSettings = 0;
@@ -716,6 +719,8 @@ int unp64(const byte *compressed, uint32 length, byte *destinationBuffer, uint32
if (unp._endAdr < unp._strMem)
unp._endAdr = 0x10000;
+ if (unp._strMem < 2 || unp._strMem >= unp._endAdr)
+ return 0;
mem[unp._strMem - 2] = unp._strMem & 0xff;
mem[unp._strMem - 1] = unp._strMem >> 8;
diff --git a/engines/freescape/configure.engine b/engines/freescape/configure.engine
index 6afbc3b536c..193df84aea0 100644
--- a/engines/freescape/configure.engine
+++ b/engines/freescape/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine freescape "Freescape" yes "" "" "highres 16bit 3d" "tinygl sid_audio"
+add_engine freescape "Freescape" yes "" "" "highres 16bit 3d unp64" "tinygl sid_audio"
diff --git a/engines/freescape/games/castle/c64.cpp b/engines/freescape/games/castle/c64.cpp
index 6cf1461de86..b647f992ceb 100644
--- a/engines/freescape/games/castle/c64.cpp
+++ b/engines/freescape/games/castle/c64.cpp
@@ -19,6 +19,8 @@
*
*/
+#include "common/compression/unp64.h"
+#include "common/endian.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/random.h"
@@ -32,7 +34,7 @@
namespace Freescape {
enum {
- kCastleC64DatabaseOffset = 0x9951,
+ kCastleC64DatabaseAddress = 0x9d00,
kCastleC64RuntimeDemoPointerOffset = 0x42,
kCastleC64RuntimeAreaTableOffset = 0x4f,
kCastleC64CompactDemoPointerOffset = 0x3e,
@@ -69,55 +71,41 @@ static uint32 castleC64UIColor(const Graphics::PixelFormat &format, byte color)
return format.ARGBToColor(255, rgb[0], rgb[1], rgb[2]);
}
-static Common::Array<byte> unpackCastleC64UI(Common::SeekableReadStream *file) {
- // The startup relocates the packed stream from $0d50 to $2708, then
- // expands it into $0200..$ffff. Decode only through the screen attributes
- // at $c400..$c7e7: the later bitmap pages require the separate tape loader.
- // Page flags at $09ff descend through memory, least significant bit first.
- // A clear bit selects a page with its own escape byte and count/value runs.
+static Common::Array<byte> unpackCastleC64(Common::SeekableReadStream *file) {
+ if (file->size() < 2 || file->size() > 0x10000)
+ error("Invalid Castle C64 program size");
+
Common::Array<byte> packed;
packed.resize(file->size());
file->seek(0);
- if (packed.size() < 0x551 || file->read(packed.data(), packed.size()) != packed.size())
- error("Unable to read Castle C64 UI data");
+ if (file->read(packed.data(), packed.size()) != packed.size() || READ_LE_UINT16(packed.data()) != 0x0801)
+ error("Unable to read Castle C64 program");
+
+ // The split tape loader preserves the rest of the snapshot at $d000.
+ Common::File loader;
+ if (loader.open("castlemaster.c64.loader")) {
+ const uint32 sourceOffset = 2 + 0x0847 - 0x0801;
+ const uint32 destinationOffset = 2 + 0xd000 - 0x0801;
+ if (loader.size() <= sourceOffset || loader.size() - sourceOffset > 0x3000 ||
+ loader.readUint16LE() != 0x0801 || packed.size() > destinationOffset)
+ error("Invalid Castle C64 split loader");
+ uint32 size = loader.size() - sourceOffset;
+ packed.resize(destinationOffset + size);
+ loader.seek(sourceOffset);
+ if (loader.read(packed.data() + destinationOffset, size) != size)
+ error("Truncated Castle C64 split loader");
+ }
Common::Array<byte> data;
- data.resize(0xc800);
- uint32 source = 0x551; // $0d50, including the PRG load-address adjustment
- int flagOffset = 0x200; // $09ff
- byte flags = packed[flagOffset];
- int bitsLeft = 6; // The first two pages are not part of the packed stream.
- for (uint page = 2; page < 0xc8; page++) {
- if (!bitsLeft) {
- flags = packed[--flagOffset];
- bitsLeft = 8;
- }
- bool raw = flags & 1;
- flags >>= 1;
- bitsLeft--;
- uint end = (page + 1) * 256;
- if (source >= packed.size())
- error("Truncated Castle C64 UI page %x", page);
- byte escape = raw ? 0 : packed[source++];
- for (uint dest = page * 256; dest < end;) {
- if (source >= packed.size())
- error("Truncated Castle C64 UI page %x", page);
- byte value = packed[source++];
- uint count = 1;
- if (!raw && value == escape) {
- if (source + 2 > packed.size())
- error("Truncated Castle C64 UI run");
- count = packed[source++];
- if (!count)
- count = 256;
- value = packed[source++];
- }
- if (count > end - dest)
- error("Castle C64 UI run crosses a page boundary");
- while (count--)
- data[dest++] = value;
- }
- }
+ data.resize(0x10000);
+ uint32 size = 0;
+ if (!Common::Unp64::unp64(packed.data(), packed.size(), data.data(), &size, nullptr) || size < 2)
+ error("Unable to unpack Castle C64 snapshot");
+ uint32 address = READ_LE_UINT16(data.data());
+ if (address > 0x1401 || size - 2 > data.size() - address || address + size - 2 < 0xc800)
+ error("Incomplete Castle C64 snapshot");
+ memmove(data.data() + address, data.data() + 2, size - 2);
+ memset(data.data(), 0, address);
return data;
}
@@ -148,32 +136,6 @@ static void loadCastleC64Frame(const Common::Array<byte> &data, uint address, Gr
loadCastleC64Bitmap(data, pixels, width, height, surface);
}
-struct CastleC64Repeat {
- uint16 offset;
- byte count;
- byte value;
-};
-
-const uint16 kCastleC64DatabaseSkips[] = {
- 0x01fc, 0x02fc, 0x05fa, 0x08f9, 0x09f9, 0x0af9, 0x0bf4, 0x0cf0,
- 0x0deb, 0x0fe9, 0x10e5, 0x11e3, 0x13e1, 0x14de, 0x18dd, 0x19db,
- 0x1cd9, 0x1ed7, 0x20cf, 0x21c8
-};
-
-const CastleC64Repeat kCastleC64DatabaseRepeats[] = {
- { 0x0006, 4, 0x00 }, { 0x0009, 4, 0xff }, { 0x0010, 4, 0x55 }, { 0x001b, 4, 0xaa },
- { 0x02dd, 4, 0x02 }, { 0x0327, 4, 0x00 }, { 0x0347, 4, 0x01 }, { 0x0355, 4, 0x00 },
- { 0x05fe, 4, 0x00 }, { 0x0610, 4, 0x01 }, { 0x0936, 4, 0x00 }, { 0x0aca, 4, 0x00 },
- { 0x0b58, 6, 0x00 }, { 0x0bd9, 6, 0x00 }, { 0x0c5a, 4, 0x01 }, { 0x0c69, 4, 0x01 },
- { 0x0c78, 4, 0x02 }, { 0x0c87, 4, 0x02 }, { 0x0c95, 4, 0x00 }, { 0x0d43, 6, 0x00 },
- { 0x0d9e, 6, 0x00 }, { 0x0ec9, 6, 0x00 }, { 0x0fea, 5, 0x00 }, { 0x1085, 6, 0x00 },
- { 0x1163, 6, 0x00 }, { 0x127e, 6, 0x00 }, { 0x140b, 4, 0x06 }, { 0x1497, 6, 0x00 },
- { 0x1543, 5, 0x00 }, { 0x1910, 6, 0x00 }, { 0x1a1b, 6, 0x00 }, { 0x1da9, 6, 0x00 },
- { 0x1edb, 6, 0x00 }, { 0x1ee1, 6, 0x00 }, { 0x1ee7, 6, 0x00 }, { 0x20d0, 4, 0x00 },
- { 0x2128, 6, 0x00 }, { 0x217e, 6, 0x00 }, { 0x21c5, 4, 0x00 }, { 0x2250, 6, 0x00 },
- { 0x2255, 113, 0x00 }
-};
-
uint16 readCastleC64Uint16LE(const Common::Array<byte> &data, uint32 offset) {
if (offset + 1 >= data.size())
error("Castle C64 database pointer read out of range at 0x%x", offset);
@@ -181,60 +143,6 @@ uint16 readCastleC64Uint16LE(const Common::Array<byte> &data, uint32 offset) {
return data[offset] | (data[offset + 1] << 8);
}
-Common::Array<byte> normalizeCastleC64Database(Common::SeekableReadStream *file) {
- file->seek(kCastleC64DatabaseOffset);
- if (file->pos() != kCastleC64DatabaseOffset)
- error("Unable to seek to Castle C64 database at 0x%x", kCastleC64DatabaseOffset);
- if (file->size() <= kCastleC64DatabaseOffset)
- error("Castle C64 database file is too short");
-
- uint32 rawSize = file->size() - kCastleC64DatabaseOffset;
- Common::Array<byte> raw;
- raw.resize(rawSize);
- if (file->read(&raw[0], rawSize) != rawSize)
- error("Unable to read Castle C64 database");
- if (raw.size() < 3)
- error("Castle C64 database is too short");
-
- const uint16 decodedSize = readCastleC64Uint16LE(raw, 1);
- Common::Array<byte> decoded;
- uint32 sourceOffset = 0;
- uint skipIndex = 0;
- uint repeatIndex = 0;
-
- while (decoded.size() < decodedSize) {
- if (sourceOffset >= raw.size())
- error("Castle C64 database normalization ran out of source data");
-
- if (skipIndex < ARRAYSIZE(kCastleC64DatabaseSkips) && sourceOffset == kCastleC64DatabaseSkips[skipIndex]) {
- sourceOffset++;
- skipIndex++;
- continue;
- }
-
- if (repeatIndex < ARRAYSIZE(kCastleC64DatabaseRepeats) && sourceOffset == kCastleC64DatabaseRepeats[repeatIndex].offset) {
- const CastleC64Repeat &repeat = kCastleC64DatabaseRepeats[repeatIndex];
- if (sourceOffset + 2 >= raw.size() || raw[sourceOffset + 1] != repeat.count || raw[sourceOffset + 2] != repeat.value)
- error("Castle C64 database repeat mismatch at 0x%x", sourceOffset);
-
- for (uint i = 0; i < repeat.count && decoded.size() < decodedSize; i++)
- decoded.push_back(repeat.value);
-
- sourceOffset += 3;
- repeatIndex++;
- continue;
- }
-
- decoded.push_back(raw[sourceOffset++]);
- }
-
- if (skipIndex != ARRAYSIZE(kCastleC64DatabaseSkips) || repeatIndex != ARRAYSIZE(kCastleC64DatabaseRepeats))
- error("Castle C64 database normalization did not consume all relocation entries");
-
- debugC(1, kFreescapeDebugParser, "Castle C64 normalized database: 0x%x -> 0x%x bytes", sourceOffset, decodedSize);
- return decoded;
-}
-
static Common::Array<Graphics::ManagedSurface *> loadCastleC64Font(const Common::Array<byte> &data) {
Common::Array<Graphics::ManagedSurface *> chars;
@@ -266,7 +174,7 @@ class CastleC64DatabaseReadStream : public Common::SeekableReadStream {
public:
CastleC64DatabaseReadStream(const Common::Array<byte> &data) : _data(data), _pos(0), _eos(false), _colorMapRead(false) {
if (_data.size() < kCastleC64RuntimeAreaTableOffset)
- error("Castle C64 normalized database is too short");
+ error("Castle C64 database is too short");
for (uint i = 0; i < 4; i++)
_compactPointerBytes[i] = _data[kCastleC64RuntimeDemoPointerOffset + i];
@@ -456,7 +364,7 @@ void CastleEngine::loadAssetsC64FullGame() {
if (!file.isOpen())
error("Failed to open castlemaster.c64.data");
- Common::Array<byte> uiData = unpackCastleC64UI(&file);
+ Common::Array<byte> uiData = unpackCastleC64(&file);
Common::MemoryReadStream uiStream(uiData.data(), uiData.size());
Common::Array<Graphics::ManagedSurface *> chars = loadCastleC64Font(uiData);
_font = Font(chars);
@@ -576,7 +484,10 @@ void CastleEngine::loadAssetsC64FullGame() {
_flagFrames.push_back(flag);
}
- Common::Array<byte> database = normalizeCastleC64Database(&file);
+ uint16 databaseSize = readCastleC64Uint16LE(uiData, kCastleC64DatabaseAddress + 1);
+ if (databaseSize < kCastleC64RuntimeAreaTableOffset || kCastleC64DatabaseAddress + databaseSize > uiData.size())
+ error("Invalid Castle C64 database size");
+ Common::Array<byte> database(uiData.data() + kCastleC64DatabaseAddress, databaseSize);
CastleC64DatabaseReadStream databaseStream(database);
load8bitBinary(&databaseStream, 0, 16);
@@ -619,7 +530,7 @@ void CastleEngine::loadAssetsC64FullGame() {
_sound = createCastleC64Sound(_mixer, uiData);
_playerMusic = new CastleC64MusicPlayer(_mixer);
- // TODO: title screen is in BASIC loader (file 009) - not yet extracted
+ // TODO: Extract the title screen from the snapshot.
}
void CastleEngine::drawC64HudSurface(Graphics::Surface *surface, const Graphics::Surface &frame, const Common::Point &origin) {
Commit: 6efdeb5b6ff0f55d6a0ac9810f2a4f16ae158a55
https://github.com/scummvm/scummvm/commit/6efdeb5b6ff0f55d6a0ac9810f2a4f16ae158a55
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-08T22:11:14+02:00
Commit Message:
FREESCAPE: decompress some freescape games using unp64
Changed paths:
engines/freescape/detection.cpp
engines/freescape/freescape.h
engines/freescape/games/dark/c64.cpp
engines/freescape/games/eclipse/c64.cpp
engines/freescape/loaders/c64.cpp
diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 8dae99dcd0f..fe0333fe2d2 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -535,7 +535,8 @@ const ADGameDescription gameDescriptions[] = {
{
"darkside", // Tape release
"",
- AD_ENTRY1s("DARKSIDE.C64.DATA", "7d5fc9a962a146e303a0c71a2d5c651e", 48129),
+ AD_ENTRY2s("DARKSIDE.C64.DATA", "7d5fc9a962a146e303a0c71a2d5c651e", 48129,
+ "DARKSIDE.C64.DATA2", "b0c66200fcd60cf746f00bd7f9177d70", 2682),
Common::EN_ANY,
Common::kPlatformC64,
GF_C64_TAPE,
@@ -668,7 +669,8 @@ const ADGameDescription gameDescriptions[] = {
{
"totaleclipse2", // Tape release
"",
- AD_ENTRY1s("TOTALECLIPSE2.C64.DATA", "7ab839a4260c197f24b41ef6ab45ef21", 47105),
+ AD_ENTRY2s("TOTALECLIPSE2.C64.DATA", "7ab839a4260c197f24b41ef6ab45ef21", 47105,
+ "TOTALECLIPSE2.C64.DATA2", "0c1ddf1de6b8995d52bfb21abed062f4", 2818),
Common::EN_ANY,
Common::kPlatformC64,
ADGF_UNSTABLE | GF_C64_TAPE,
@@ -728,7 +730,8 @@ const ADGameDescription gameDescriptions[] = {
{
"totaleclipse", // Tape relese
"",
- AD_ENTRY1s("TOTALECLIPSE.C64.DATA", "968fd46b941a00f887741dfc348ac149", 47105),
+ AD_ENTRY2s("TOTALECLIPSE.C64.DATA", "968fd46b941a00f887741dfc348ac149", 47105,
+ "TOTALECLIPSE.C64.DATA2", "c45f16800b83c8dc4e0bcdbc3a4a75ac", 4097),
Common::EN_ANY,
Common::kPlatformC64,
ADGF_TESTING | GF_C64_TAPE,
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index b0310b1948e..6994fdf6f78 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -682,7 +682,7 @@ public:
Common::RandomSource *_rnd;
// C64 specifics
- byte *decompressC64RLE(byte *buffer, int *size, byte marker);
+ Common::Array<byte> unpackC64Snapshot(Common::SeekableReadStream *file, const Common::Path &continuation);
byte *_extraBuffer;
};
diff --git a/engines/freescape/games/dark/c64.cpp b/engines/freescape/games/dark/c64.cpp
index b872c054e94..3b2c3560b15 100644
--- a/engines/freescape/games/dark/c64.cpp
+++ b/engines/freescape/games/dark/c64.cpp
@@ -215,22 +215,16 @@ void DarkEngine::loadAssetsC64FullGame() {
file.open("darkside.c64.data");
if (_variant & GF_C64_TAPE) {
- int size = file.size();
-
- byte *buffer = (byte *)malloc(size * sizeof(byte));
- file.read(buffer, file.size());
-
- _extraBuffer = decompressC64RLE(buffer, &size, 0xdf);
- // size should be the size of the decompressed data
- Common::MemoryReadStream dfile(_extraBuffer, size, DisposeAfterUse::NO);
-
- loadMessagesFixedSize(&dfile, 0x1edf, 16, 27);
- loadFonts(&dfile, 0xc3e);
- loadGlobalObjects(&dfile, 0x20bd, 23);
- load8bitBinary(&dfile, 0x9b3e, 16);
- loadDarkC64CompassTable(&dfile, 0x7e37, _c64CompassTable);
- loadDarkC64Indicators(&dfile, 0xadba, _indicators, _gfx->_texturePixelFormat);
- loadDarkC64ModeFrames(&dfile, 0xd2f6, _c64ModeFrames, _gfx->_texturePixelFormat);
+ Common::Array<byte> data = unpackC64Snapshot(&file, "darkside.c64.data2");
+ Common::MemoryReadStream dfile(data.data(), data.size(), DisposeAfterUse::NO);
+
+ loadMessagesFixedSize(&dfile, 0x1aa1, 16, 27);
+ loadFonts(&dfile, 0x0800);
+ loadGlobalObjects(&dfile, 0x1c7f, 23);
+ load8bitBinary(&dfile, 0x9700, 16);
+ loadDarkC64CompassTable(&dfile, 0x79f9, _c64CompassTable);
+ loadDarkC64Indicators(&dfile, 0xa97c, _indicators, _gfx->_texturePixelFormat);
+ loadDarkC64ModeFrames(&dfile, 0xceb8, _c64ModeFrames, _gfx->_texturePixelFormat);
} else if (_variant & GF_C64_DISC) {
loadMessagesFixedSize(&file, 0x16a3, 16, 27);
loadFonts(&file, 0x402);
diff --git a/engines/freescape/games/eclipse/c64.cpp b/engines/freescape/games/eclipse/c64.cpp
index 30ef6458d62..19c609432b5 100644
--- a/engines/freescape/games/eclipse/c64.cpp
+++ b/engines/freescape/games/eclipse/c64.cpp
@@ -58,20 +58,15 @@ extern byte kC64Palette[16][3];
void EclipseEngine::loadAssetsC64FullGame() {
Common::File file;
file.open(isEclipse2() ? "totaleclipse2.c64.data" : "totaleclipse.c64.data");
+ Common::Array<byte> data;
if (_variant & GF_C64_TAPE) {
- int size = file.size();
+ data = unpackC64Snapshot(&file, isEclipse2() ? "totaleclipse2.c64.data2" : "totaleclipse.c64.data2");
+ Common::MemoryReadStream dfile(data.data(), data.size(), DisposeAfterUse::NO);
- byte *buffer = (byte *)malloc(size * sizeof(byte));
- file.read(buffer, file.size());
-
- _extraBuffer = decompressC64RLE(buffer, &size, isEclipse2() ? 0xd2 : 0xe1);
- // size should be the size of the decompressed data
- Common::MemoryReadStream dfile(_extraBuffer, size, DisposeAfterUse::NO);
-
- loadMessagesFixedSize(&dfile, 0x1d84, 16, isEclipse2() ? 34 : 30);
- loadFonts(&dfile, 0xc3e);
- load8bitBinary(&dfile, 0x9a3e, 16);
+ loadMessagesFixedSize(&dfile, 0x1946, 16, isEclipse2() ? 34 : 30);
+ loadFonts(&dfile, 0x0800);
+ load8bitBinary(&dfile, 0x9600, 16);
} else if (_variant & GF_C64_DISC) {
loadMessagesFixedSize(&file, isEclipse2() ? 0x1538 : 0x1534, 16, isEclipse2() ? 34 : 30);
loadFonts(&file, 0x3f2);
@@ -120,14 +115,10 @@ void EclipseEngine::loadAssetsC64FullGame() {
_playerMusic = new EclipseC64MusicPlayer(_c64MusicData);
}
}
- } else if ((_variant & GF_C64_TAPE) && _extraBuffer) {
- // Tape decompressed data has music at a 0x0C3F offset from disc addresses.
- // The music player expects data indexed from load address 0x0410.
- // Remap: musicData[i] = decompressed[i + 0x084E]
- static const int kTapeMusicShift = 0x084E;
- static const int kMusicRegionSize = 0x1100; // covers 0x0410..0x14FF
+ } else if (_variant & GF_C64_TAPE) {
+ static const int kMusicRegionSize = 0x1100; // covers 0x0410..0x150f
_c64MusicData.resize(kMusicRegionSize);
- memcpy(_c64MusicData.data(), _extraBuffer + kTapeMusicShift, kMusicRegionSize);
+ memcpy(_c64MusicData.data(), data.data() + 0x0410, kMusicRegionSize);
delete _playerMusic;
_playerMusic = new EclipseC64MusicPlayer(_c64MusicData);
}
diff --git a/engines/freescape/loaders/c64.cpp b/engines/freescape/loaders/c64.cpp
index 2f7c3499f3d..c63fb710ea7 100644
--- a/engines/freescape/loaders/c64.cpp
+++ b/engines/freescape/loaders/c64.cpp
@@ -19,32 +19,49 @@
*
*/
+#include "common/compression/unp64.h"
+#include "common/endian.h"
+
#include "freescape/freescape.h"
namespace Freescape {
-byte *FreescapeEngine::decompressC64RLE(byte *buffer, int *size, byte marker) {
- Common::MemoryReadWriteStream *tmp = new Common::MemoryReadWriteStream(DisposeAfterUse::NO);
- // Format is: [ Byte, Marker, Length ] or [ Byte ]
- for (int i = 0; i < *size - 1; ) {
- if (buffer[i] == marker && i > 0) {
- int length = buffer[i + 1];
- byte value = buffer[i - 1];
- if (length == 0)
- tmp->writeByte(value);
-
- for (int j = 0; j < length; j++) {
- tmp->writeByte(value);
- }
- i += 2;
- } else {
- tmp->writeByte(buffer[i]);
- i += 1;
- }
- }
- *size = tmp->size();
- byte *data = tmp->getData();
- delete tmp;
+Common::Array<byte> FreescapeEngine::unpackC64Snapshot(Common::SeekableReadStream *file, const Common::Path &continuation) {
+ if (file->size() <= 2 || file->size() > 0x10002)
+ error("Invalid C64 program size");
+
+ Common::Array<byte> packed;
+ packed.resize(file->size());
+ file->seek(0);
+ if (file->read(packed.data(), packed.size()) != packed.size())
+ error("Unable to read C64 program");
+
+ uint32 endAddress = READ_LE_UINT16(packed.data()) + packed.size() - 2;
+ if (endAddress > 0x10000)
+ error("Invalid C64 program load address");
+
+ Common::File part;
+ if (!part.open(continuation))
+ error("Unable to open C64 continuation %s", continuation.toString().c_str());
+ if (part.size() <= 2 || part.size() - 2 > 0x10000 - endAddress || part.readUint16LE() != endAddress)
+ error("Invalid C64 continuation %s", continuation.toString().c_str());
+
+ uint32 offset = packed.size();
+ uint32 partSize = part.size() - 2;
+ packed.resize(offset + partSize);
+ if (part.read(packed.data() + offset, partSize) != partSize)
+ error("Truncated C64 continuation %s", continuation.toString().c_str());
+
+ Common::Array<byte> data;
+ data.resize(0x10000);
+ uint32 size = 0;
+ if (!Common::Unp64::unp64(packed.data(), packed.size(), data.data(), &size, nullptr))
+ error("Unable to unpack C64 snapshot");
+ if (size != data.size() || READ_LE_UINT16(data.data()) != 2)
+ error("Incomplete C64 snapshot");
+
+ // The PRG covers $0002..$ffff, so its payload is already at the RAM offsets.
+ data[0] = data[1] = 0;
return data;
}
Commit: 57c0ce99a60ff387b19f4b8c57ad1a2aa51fd685
https://github.com/scummvm/scummvm/commit/57c0ce99a60ff387b19f4b8c57ad1a2aa51fd685
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-08T22:11:14+02:00
Commit Message:
FREESCAPE: decompress driller c64 releases using unp64
Changed paths:
engines/freescape/detection.cpp
engines/freescape/freescape.h
engines/freescape/games/driller/c64.cpp
engines/freescape/loaders/c64.cpp
diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index fe0333fe2d2..0b2765dfeb7 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -110,12 +110,12 @@ const ADGameDescription gameDescriptions[] = {
GUIO3(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING, GUIO_RENDERC64)
},
{
- "driller", // Tape re-relase
+ "driller", // Tape rerelease
"",
- AD_ENTRY1s("DRILLER.C64.DATA", "4afec6eea3887343e7f91fb21a2f2948", 43278),
+ AD_ENTRY1s("DRILLER.C64.DATA", "4d7ed1343f9cd522447602bf402c79a6", 56329),
Common::EN_ANY,
Common::kPlatformC64,
- ADGF_UNSUPPORTED, //| GF_C64_TAPE,
+ ADGF_UNSTABLE | GF_C64_TAPE | GF_C64_PACKED,
GUIO3(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING, GUIO_RENDERC64)
},
{
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 6994fdf6f78..dc24f83e743 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -682,6 +682,7 @@ public:
Common::RandomSource *_rnd;
// C64 specifics
+ Common::Array<byte> unpackC64Snapshot(const Common::Array<byte> &packed);
Common::Array<byte> unpackC64Snapshot(Common::SeekableReadStream *file, const Common::Path &continuation);
byte *_extraBuffer;
};
@@ -700,6 +701,7 @@ enum GameReleaseFlags {
GF_ATARI_BUDGET = (1 << 10),
GF_C64_TAPE = (1 << 11),
GF_C64_DISC = (1 << 12),
+ GF_C64_PACKED = (1 << 13),
};
extern FreescapeEngine *g_freescape;
diff --git a/engines/freescape/games/driller/c64.cpp b/engines/freescape/games/driller/c64.cpp
index 7d82ada1112..1181352290c 100644
--- a/engines/freescape/games/driller/c64.cpp
+++ b/engines/freescape/games/driller/c64.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/endian.h"
#include "common/file.h"
#include "freescape/freescape.h"
@@ -29,6 +30,36 @@ namespace Freescape {
extern byte kC64Palette[16][3];
+static Common::Array<byte> loadDrillerC64PackedProgram(Common::SeekableReadStream *file) {
+ static const struct {
+ uint16 loadAddress;
+ uint16 address;
+ uint16 size;
+ } parts[] = {
+ {0x0400, 0x0400, 2112},
+ {0x0c40, 0x0c40, 43276},
+ {0xb54c, 0xb54c, 4788},
+ {0xc800, 0xc800, 2047},
+ // The tape loader moves this block to $d000 before loading the title.
+ {0x4000, 0xd000, 4096}
+ };
+
+ if (file->size() != 56329)
+ error("Invalid packed Driller C64 program size");
+
+ Common::Array<byte> packed;
+ packed.resize(0xe000 - 0x0400 + 2);
+ WRITE_LE_UINT16(packed.data(), 0x0400);
+ // Concatenated PRGs retain their two-byte load addresses.
+ for (const auto &part : parts) {
+ if (file->readUint16LE() != part.loadAddress)
+ error("Invalid Driller C64 tape block at $%04x", part.loadAddress);
+ if (file->read(packed.data() + part.address - 0x0400 + 2, part.size) != part.size)
+ error("Truncated Driller C64 tape block at $%04x", part.loadAddress);
+ }
+ return packed;
+}
+
void DrillerEngine::initC64() {
_viewArea = Common::Rect(32, 16, 288, 120);
}
@@ -42,18 +73,22 @@ void DrillerEngine::loadAssetsC64FullGame() {
load8bitBinary(&file, 0x8e02, 4);
loadGlobalObjects(&file, 0x1855, 8);
} else if (_targetName.hasPrefix("driller")) {
- file.open("driller.c64.data");
-
- if (_variant) {
+ if (!file.open("driller.c64.data"))
+ error("Unable to open driller.c64.data");
+
+ if (_variant & GF_C64_PACKED) {
+ Common::Array<byte> packed = loadDrillerC64PackedProgram(&file);
+ Common::Array<byte> data = unpackC64Snapshot(packed);
+ Common::MemoryReadStream stream(data.data(), data.size(), DisposeAfterUse::NO);
+ loadMessagesFixedSize(&stream, 0x1a78, 14, 20);
+ loadGlobalObjects(&stream, 0x1c53, 8);
+ loadFonts(&stream, 0x0800);
+ load8bitBinary(&stream, 0x9200, 16);
+ } else if (_variant & (GF_C64_TAPE | GF_C64_DISC)) {
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);
diff --git a/engines/freescape/loaders/c64.cpp b/engines/freescape/loaders/c64.cpp
index c63fb710ea7..682a5e810b5 100644
--- a/engines/freescape/loaders/c64.cpp
+++ b/engines/freescape/loaders/c64.cpp
@@ -52,6 +52,10 @@ Common::Array<byte> FreescapeEngine::unpackC64Snapshot(Common::SeekableReadStrea
if (part.read(packed.data() + offset, partSize) != partSize)
error("Truncated C64 continuation %s", continuation.toString().c_str());
+ return unpackC64Snapshot(packed);
+}
+
+Common::Array<byte> FreescapeEngine::unpackC64Snapshot(const Common::Array<byte> &packed) {
Common::Array<byte> data;
data.resize(0x10000);
uint32 size = 0;
Commit: 56000821acf65621319f30f4ca42f5295661fc62
https://github.com/scummvm/scummvm/commit/56000821acf65621319f30f4ca42f5295661fc62
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-09-08T22:11:14+02:00
Commit Message:
COLONY: various forklift behavior fixes
Changed paths:
engines/colony/animation.cpp
engines/colony/colony.h
engines/colony/console.cpp
engines/colony/interaction.cpp
engines/colony/movement.cpp
engines/colony/sound.cpp
diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 217fb1ae837..e5cf29d88b7 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -372,6 +372,28 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
return true;
}
+bool ColonyEngine::loadLiftAnimation(int objectType) {
+ switch (objectType) {
+ case kObjTeleport:
+ _liftObject = 1;
+ break;
+ case kObjBox1:
+ case kObjBox2:
+ _liftObject = 2;
+ break;
+ case kObjCryo:
+ _liftObject = 3;
+ break;
+ case kObjReactor:
+ _liftObject = 4;
+ break;
+ default:
+ return false;
+ }
+
+ return loadAnimation("lift");
+}
+
void ColonyEngine::deleteAnimation() {
delete _backgroundMask;
_backgroundMask = nullptr;
@@ -551,28 +573,10 @@ void ColonyEngine::playAnimation() {
setObjectOnOff(4, false);
}
} else if (_animationName == "lift") {
- // Original DoLift: set up initial state based on forklift mode.
- // _fl==1 â picking up (up=0, object starts at bottom)
- // _fl==2 â putting down (up=1, object starts at top)
- // Object sprite mapping: BOX1/BOX2â2, TELEPORTâ1, CRYOâ3, REACTORâ4
+ if (getPlatform() == Common::kPlatformMacintosh)
+ _sound->stop();
+
_liftUp = (_fl == 2);
- switch (_fl == 2 ? _carryType : 0) {
- case kObjBox1: case kObjBox2: _liftObject = 2; break;
- case kObjTeleport: _liftObject = 1; break;
- case kObjCryo: _liftObject = 3; break;
- case kObjReactor: _liftObject = 4; break;
- default: _liftObject = 2; break; // pickup: we don't know yet, but dolSprite handles it
- }
- // For pickup, determine object from what we're about to pick up
- if (!_liftUp) {
- // The interaction code sets _carryType AFTER the animation,
- // but the object type is in the Thing we're interacting with.
- // We can infer from which sprites are visible.
- for (int i = 1; i <= 4; i++) {
- if (i < (int)_lSprites.size() && _lSprites[i - 1] && _lSprites[i - 1]->onoff)
- _liftObject = i;
- }
- }
// Hide all object sprites except the active one
for (int i = 1; i <= 4; i++) {
if (i != _liftObject)
@@ -725,6 +729,13 @@ void ColonyEngine::playAnimation() {
_system->delayMillis(2);
}
+ if (_animationName == "lift" && getPlatform() == Common::kPlatformMacintosh) {
+ // Mac KillTSound waits for the lift sample before returning.
+ while (_sound->isPlaying() && !shouldQuit())
+ responsiveAnimationDelay(_system, 10);
+ _sound->stop();
+ }
+
if (useSquarePixelViewport)
_gfx->setSquarePixelViewport(false);
_system->lockMouse(true);
@@ -1478,6 +1489,7 @@ void ColonyEngine::handleAnimationClick(int item) {
// item 8 = lower button (active when _liftUp)
// item 9 = raise button (active when !_liftUp)
if (item == 8 && _liftUp) {
+ _sound->play(Sound::kDrop);
// Lower the object: animate states 5â1
setObjectState(8, 2); // lower arrow OFF
setObjectState(9, 1); // raise arrow ON
@@ -1490,6 +1502,7 @@ void ColonyEngine::handleAnimationClick(int item) {
_liftUp = false;
_animationResult = 1;
} else if (item == 9 && !_liftUp) {
+ _sound->play(Sound::kLift);
// Raise the object: animate states 1â5
setObjectState(9, 2); // raise arrow OFF
setObjectState(8, 1); // lower arrow ON
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index c063e59b80a..58bbb585aee 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -795,7 +795,7 @@ private:
bool patchMapFrom(const PassPatch &from, uint8 *mapdata);
void exitForklift();
void dropCarriedObject();
- bool stepOutOfCell();
+ bool stepOutOfCell(uint8 angle, bool backwards = false);
bool exitTeleport();
void teleportPlayer();
bool setDoorState(int x, int y, int direction, int state);
@@ -914,6 +914,7 @@ private:
bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr, bool gameOver = false);
bool drawPict(int resID);
bool loadAnimation(const Common::String &name);
+ bool loadLiftAnimation(int objectType);
void deleteAnimation();
void takeOff();
void fullOfStars();
diff --git a/engines/colony/console.cpp b/engines/colony/console.cpp
index a109fe08579..a39cfc43ab1 100644
--- a/engines/colony/console.cpp
+++ b/engines/colony/console.cpp
@@ -337,20 +337,19 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
}
}
- // Clear player from current robot array position
- if (_vm->_me.xindex >= 0 && _vm->_me.xindex < 32 &&
- _vm->_me.yindex >= 0 && _vm->_me.yindex < 32)
- _vm->_robotArray[_vm->_me.xindex][_vm->_me.yindex] = 0;
+ _vm->clearPlayerCellMarker();
// Load the target level
- if (level != _vm->_level)
+ if (level != _vm->_level) {
_vm->loadMap(level);
+ _vm->clearPlayerCellMarker();
+ }
// If no coordinates given, scan for an entry point (stairs/tunnel/elevator)
if (targetX < 0) {
for (int x = 0; x < 31 && targetX < 0; x++) {
for (int y = 0; y < 31 && targetX < 0; y++) {
- for (int d = 0; d < 5; d++) {
+ for (int d = 0; d < 4; d++) {
int feat = _vm->_mapData[x][y][d][0];
if (feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs ||
feat == kWallFeatureTunnel || feat == kWallFeatureElevator) {
@@ -377,8 +376,8 @@ bool Debugger::cmdTeleport(int argc, const char **argv) {
_vm->_me.xloc = (targetX << 8) + 128;
_vm->_me.yloc = (targetY << 8) + 128;
- // Register player in robot array
- _vm->_robotArray[targetX][targetY] = kMeNum;
+ _vm->setPlayerCellMarker();
+ _vm->_bumpedObject = 0;
debugPrintf("Teleported to level %d at (%d, %d)\n", _vm->_level, targetX, targetY);
return false;
@@ -697,6 +696,7 @@ bool Debugger::cmdForklift(int argc, const char **argv) {
return true;
}
_vm->_fl = state;
+ _vm->_bumpedObject = 0;
if (state > 0)
_vm->_me.lookY = 0; // reset vertical look
if (state == 0) {
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 15d7aac0a82..839abb076bc 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -116,8 +116,7 @@ void ColonyEngine::interactWithObject(int objNum) {
{
if (_fl == 1) {
// In empty forklift pick up the teleporter itself
- if (loadAnimation("lift")) {
- _sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
+ if (loadLiftAnimation(obj.type)) {
_animationResult = 0;
playAnimation();
if (_animationResult) {
@@ -213,8 +212,7 @@ void ColonyEngine::interactWithObject(int objNum) {
case kObjCryo:
if (_fl == 1) {
// In empty forklift pick up object
- if (loadAnimation("lift")) {
- _sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
+ if (loadLiftAnimation(obj.type)) {
_animationResult = 0;
playAnimation();
if (_animationResult) {
@@ -246,8 +244,7 @@ void ColonyEngine::interactWithObject(int objNum) {
case kObjReactor:
if (_fl == 1 && _coreState[_coreIndex] == 1) {
// Empty forklift at open reactor pick up reactor core
- if (loadAnimation("lift")) {
- _sound->play(Sound::kLift); // GANIMATE.C DoLift: DoLiftSound()
+ if (loadLiftAnimation(obj.type)) {
_animationResult = 0;
playAnimation();
if (_animationResult) {
@@ -260,8 +257,7 @@ void ColonyEngine::interactWithObject(int objNum) {
}
} else if (_fl == 2 && _carryType == kObjReactor && _coreState[_coreIndex] == 2) {
// Carrying reactor core drop it into reactor
- if (loadAnimation("lift")) {
- _sound->play(Sound::kDrop); // GANIMATE.C DoLift: DoDropSound()
+ if (loadLiftAnimation(_carryType)) {
_animationResult = 0;
playAnimation();
if (_animationResult) {
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 3df6164f05f..dae6fafb879 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -1369,22 +1369,28 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
_suppressCollisionSound = false;
}
-// DOS Forward(): inch ahead along _me.ang until the player leaves the cell.
-bool ColonyEngine::stepOutOfCell() {
+// DOS Forward(), ExitFL() and DropFL(): leave the current cell.
+bool ColonyEngine::stepOutOfCell(uint8 angle, bool backwards) {
const int xindex = _me.xindex;
const int yindex = _me.yindex;
+ const int direction = backwards ? -1 : 1;
+ clearPlayerCellMarker();
// clampToWalls() can pin the player short of the boundary, so cap the walk.
int guard = 16;
_me.type = 2; // temporary small collision type
while (_me.xindex == xindex && _me.yindex == yindex) {
- if (--guard < 0 || checkwall(_me.xloc + _cost[_me.ang], _me.yloc + _sint[_me.ang], &_me)) {
+ const int xnew = _me.xloc + direction * _cost[angle];
+ const int ynew = _me.yloc + direction * _sint[angle];
+ if (--guard < 0 || checkwall(xnew, ynew, &_me)) {
_sound->play(Sound::kChime);
_me.type = kMeNum;
+ setPlayerCellMarker();
return false;
}
}
_me.type = kMeNum;
+ setPlayerCellMarker();
return true;
}
@@ -1396,14 +1402,10 @@ bool ColonyEngine::exitTeleport() {
const int xindex = _me.xindex;
const int yindex = _me.yindex;
- // goToDestination() stamped this cell as the player's; occupiedObjectAt()
- // would read that marker back as a blocker.
- clearPlayerCellMarker();
-
_me.ang = 48;
bool out = false;
for (int tries = 0; tries < 4 && !out; tries++) {
- out = stepOutOfCell();
+ out = stepOutOfCell(_me.ang);
if (!out) {
_me.xloc = xloc;
_me.yloc = yloc;
@@ -1446,18 +1448,8 @@ void ColonyEngine::exitForklift() {
int xindex = _me.xindex;
int yindex = _me.yindex;
- // Walk backward until we move into a different cell
- while (_me.xindex == xindex && _me.yindex == yindex) {
- int xnew = _me.xloc - _cost[_me.ang];
- int ynew = _me.yloc - _sint[_me.ang];
- _me.type = 2; // temporary small collision type
- if (checkwall(xnew, ynew, &_me)) {
- _sound->play(Sound::kChime);
- _me.type = kMeNum;
- return;
- }
- _me.type = kMeNum;
- }
+ if (!stepOutOfCell(_me.look, true))
+ return;
// Snap to cell center for the dropped forklift
xloc = (xloc >> 8);
@@ -1495,9 +1487,8 @@ void ColonyEngine::dropCarriedObject() {
return;
}
- // Play the drop animation â GANIMATE.C DoLift: DoDropSound()
- if (loadAnimation("lift")) {
- _sound->play(Sound::kDrop);
+ // Play the drop animation.
+ if (loadLiftAnimation(_carryType)) {
_animationResult = 0;
playAnimation();
if (!_animationResult) {
@@ -1511,18 +1502,8 @@ void ColonyEngine::dropCarriedObject() {
int xindex = _me.xindex;
int yindex = _me.yindex;
- // Walk backward until we move into a different cell
- while (_me.xindex == xindex && _me.yindex == yindex) {
- int xnew = _me.xloc - _cost[_me.ang];
- int ynew = _me.yloc - _sint[_me.ang];
- _me.type = 2;
- if (checkwall(xnew, ynew, &_me)) {
- _sound->play(Sound::kChime);
- _me.type = kMeNum;
- return;
- }
- _me.type = kMeNum;
- }
+ if (!stepOutOfCell(_me.look, true))
+ return;
// DOS: teleport always drops at ang=0; other objects use player's angle
uint8 ang = (_carryType == kObjTeleport) ? 0 : _me.ang;
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 82d232786e0..85f3cc5b8ba 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -247,22 +247,17 @@ void Sound::playPCSpeaker(int soundID) {
break;
}
case kLift:
- {
- uint32 div = 4649;
- queueTick(div, 1);
- while (div > 3103) {
- div -= 8;
- queueTick(div, 1);
- }
- break;
- }
case kDrop:
{
- uint32 div = 3103;
- queueTick(div, 1);
- while (div < 4649) {
- div += 8;
- queueTick(div, 1);
+ // VSP uses PIT divisor 0x4000; DURATION 1 waits two interrupts.
+ const uint32 stepUs = uint64(0x4000) * 2 * 1000000 / 1193180;
+ const bool lifting = soundID == kLift;
+ const int step = lifting ? -8 : 8;
+ int div = lifting ? 4649 : 3103;
+ _speaker->playQueue(Audio::PCSpeaker::kWaveFormSquare, 1193180.0f / div, stepUs);
+ while (lifting ? div > 3103 : div < 4649) {
+ div += step;
+ _speaker->playQueue(Audio::PCSpeaker::kWaveFormSquare, 1193180.0f / div, stepUs);
}
break;
}
@@ -368,7 +363,8 @@ bool Sound::playMacSound(int soundID, bool loop) {
case kSlug: resID = 8347; break;
case kTunnel1: resID = 16403; break;
case kTunnel2: resID = 17354; break;
- case kLift: resID = 28521; break;
+ case kLift:
+ case kDrop: resID = 28521; break;
case kGlass: resID = 19944; break;
case kDoor: resID = 26867; break;
case kToilet: resID = 4955; break;
More information about the Scummvm-git-logs
mailing list