[Scummvm-git-logs] scummvm master -> eec64ea340d0c051a70fb7e20bdf33f247cf28ea
sev-
noreply at scummvm.org
Sat Dec 31 19:47:20 UTC 2022
This automated email contains information about 17 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
fdccec381d NEVERHOOD: Make most of fields of ResourceFileEntry private
0adf22278b COMMON: Uplift SafeMutexedSeekableSubReadStream to common
552420ec31 NEVERHOOD: Add support for NHC patch archives
69aa08d531 NEVERHOOD: Move game options from advanced options to custom widget
c0455314c9 NEVERHOOD: Add NHC selection item
103bb43ab2 NEVERHOOD: Load nhc file
031c929681 NEVERHOOD: Add loading of subtitle font
97b500643e NEVERHOOD: Add subtitle player
feb1d77ec7 NEVERHOOD: Show subtitles in smackerplayer
03a59a2953 NEVERHOOD: Support blitting with alphaColor != 0
f4da2d054a NEVERHOOD: Add drawDoubleSurface2Alpha
ffdf90df45 NEVERHOOD: Allocate more space for font surface
5d6ba18008 NEVERHOOD: Support subtitles for sprites
64fb44790e NEVERHOOD: Enable subtitle on/off if .nhc is available
7bb97c18d0 NEVERHOOD: Make subtitles being able to turn off/on
41aa78b8a6 NEVERHOOD: Add ability to support for repeating useful Willie's hint
eec64ea340 NEWS: Add entry about supporting Ctpax-Che at ter & Rigel localizations.
Commit: fdccec381dcd14650a0b476dd775fe99d7f92ca3
https://github.com/scummvm/scummvm/commit/fdccec381dcd14650a0b476dd775fe99d7f92ca3
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Make most of fields of ResourceFileEntry private
Changed paths:
engines/neverhood/resourceman.h
diff --git a/engines/neverhood/resourceman.h b/engines/neverhood/resourceman.h
index 339e02aa1c8..45ef6a928b0 100644
--- a/engines/neverhood/resourceman.h
+++ b/engines/neverhood/resourceman.h
@@ -30,10 +30,20 @@
namespace Neverhood {
-struct ResourceFileEntry {
+class ResourceMan;
+struct ResourceHandle;
+
+class ResourceFileEntry {
+private:
int resourceHandle;
BlbArchive *archive;
BlbArchiveEntry *archiveEntry;
+
+ friend class ResourceHandle;
+ friend class ResourceMan;
+
+public:
+ ResourceFileEntry() : resourceHandle(-1), archive(nullptr), archiveEntry(nullptr) {}
};
struct Resource {
@@ -47,8 +57,6 @@ struct ResourceData {
ResourceData() : data(NULL), dataRefCount() {}
};
-class ResourceMan;
-
struct ResourceHandle {
friend class ResourceMan;
public:
Commit: 0adf22278b45366e1b22955f64266ad369505892
https://github.com/scummvm/scummvm/commit/0adf22278b45366e1b22955f64266ad369505892
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
COMMON: Uplift SafeMutexedSeekableSubReadStream to common
Changed paths:
common/stream.cpp
common/substream.h
engines/neverhood/blbarchive.cpp
diff --git a/common/stream.cpp b/common/stream.cpp
index 176e35a9bb8..9eaf4333eb4 100644
--- a/common/stream.cpp
+++ b/common/stream.cpp
@@ -581,4 +581,9 @@ WriteStream *wrapBufferedWriteStream(WriteStream *parentStream, uint32 bufSize)
return nullptr;
}
+uint32 SafeMutexedSeekableSubReadStream::read(void *dataPtr, uint32 dataSize) {
+ Common::StackLock lock(_mutex);
+ return Common::SafeSeekableSubReadStream::read(dataPtr, dataSize);
+}
+
} // End of namespace Common
diff --git a/common/substream.h b/common/substream.h
index a6dd390ba9d..70cf88889d4 100644
--- a/common/substream.h
+++ b/common/substream.h
@@ -22,6 +22,7 @@
#ifndef COMMON_SUBSTREAM_H
#define COMMON_SUBSTREAM_H
+#include "common/mutex.h"
#include "common/ptr.h"
#include "common/stream.h"
#include "common/types.h"
@@ -133,6 +134,24 @@ public:
virtual uint32 read(void *dataPtr, uint32 dataSize);
};
+/**
+ * A special variant of SafeSeekableSubReadStream which locks a mutex during each read.
+ * This is necessary if the music is streamed from disk and it could happen
+ * that a sound effect or another music track is played from the same read stream
+ * while the first music track is updated/read.
+ */
+
+class SafeMutexedSeekableSubReadStream : public Common::SafeSeekableSubReadStream {
+public:
+ SafeMutexedSeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream,
+ Common::Mutex &mutex)
+ : SafeSeekableSubReadStream(parentStream, begin, end, disposeParentStream), _mutex(mutex) {
+ }
+ uint32 read(void *dataPtr, uint32 dataSize) override;
+protected:
+ Common::Mutex &_mutex;
+};
+
/** @} */
} // End of namespace Common
diff --git a/engines/neverhood/blbarchive.cpp b/engines/neverhood/blbarchive.cpp
index 4b5a8ac589e..53799bcb012 100644
--- a/engines/neverhood/blbarchive.cpp
+++ b/engines/neverhood/blbarchive.cpp
@@ -24,29 +24,6 @@
namespace Neverhood {
-/**
- * A special variant of SafeSeekableSubReadStream which locks a mutex during each read.
- * This is necessary because the music is streamed from disk and it could happen
- * that a sound effect or another music track is played from the same read stream
- * while the first music track is updated/read.
- */
-
-class SafeMutexedSeekableSubReadStream : public Common::SafeSeekableSubReadStream {
-public:
- SafeMutexedSeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream,
- Common::Mutex &mutex)
- : SafeSeekableSubReadStream(parentStream, begin, end, disposeParentStream), _mutex(mutex) {
- }
- uint32 read(void *dataPtr, uint32 dataSize) override;
-protected:
- Common::Mutex &_mutex;
-};
-
-uint32 SafeMutexedSeekableSubReadStream::read(void *dataPtr, uint32 dataSize) {
- Common::StackLock lock(_mutex);
- return Common::SafeSeekableSubReadStream::read(dataPtr, dataSize);
-}
-
BlbArchive::BlbArchive() : _extData(nullptr) {
}
@@ -158,7 +135,7 @@ Common::SeekableReadStream *BlbArchive::createStream(uint index) {
}
Common::SeekableReadStream *BlbArchive::createStream(BlbArchiveEntry *entry) {
- return new SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->diskSize,
+ return new Common::SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->diskSize,
DisposeAfterUse::NO, _mutex);
}
Commit: 552420ec314b781b3601e4d976d41fb31753cafa
https://github.com/scummvm/scummvm/commit/552420ec314b781b3601e4d976d41fb31753cafa
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add support for NHC patch archives
Changed paths:
A engines/neverhood/nhcarchive.cpp
A engines/neverhood/nhcarchive.h
engines/neverhood/module.mk
engines/neverhood/resource.h
engines/neverhood/resourceman.cpp
engines/neverhood/resourceman.h
diff --git a/engines/neverhood/module.mk b/engines/neverhood/module.mk
index 364df833c8b..e7b7e97167e 100644
--- a/engines/neverhood/module.mk
+++ b/engines/neverhood/module.mk
@@ -56,6 +56,7 @@ MODULE_OBJS = \
mouse.o \
navigationscene.o \
neverhood.o \
+ nhcarchive.o \
palette.o \
resource.o \
resourceman.o \
diff --git a/engines/neverhood/nhcarchive.cpp b/engines/neverhood/nhcarchive.cpp
new file mode 100644
index 00000000000..213763b3ed6
--- /dev/null
+++ b/engines/neverhood/nhcarchive.cpp
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/compression/dcl.h"
+#include "neverhood/nhcarchive.h"
+
+namespace Neverhood {
+
+bool NhcArchive::open(const Common::String &filename, bool isOptional) {
+ _entries.clear();
+
+ if (!_fd.open(filename)) {
+ if (!isOptional)
+ error("NhcArchive::open() Could not open %s", filename.c_str());
+ return false;
+ }
+
+ uint32 id = _fd.readUint32BE();
+ if (id != MKTAG('N', 'H', 'C', 0))
+ return false;
+ /* version = */ _fd.readUint32LE();
+ /* totalSize = */ _fd.readUint32LE();
+ uint32 fileCount = _fd.readUint32LE();
+
+ debug(4, "%s: fileCount = %d", filename.c_str(), fileCount);
+
+ _entries.reserve(fileCount);
+
+ // Load file hashes
+ for (uint i = 0; i < fileCount; i++) {
+ NhcArchiveEntry entry;
+ entry.fileHash = _fd.readUint32LE();
+ entry.type = _fd.readUint32LE();
+ entry.offset = _fd.readUint32LE();
+ entry.size = _fd.readUint32LE();
+ _entries.push_back(entry);
+ }
+
+ return true;
+}
+
+void NhcArchive::load(uint index, byte *buffer, uint32 size) {
+ load(&_entries[index], buffer, size);
+}
+
+void NhcArchive::load(NhcArchiveEntry *entry, byte *buffer, uint32 size) {
+ Common::StackLock lock(_mutex);
+
+ _fd.seek(entry->offset);
+
+ if (size == 0)
+ size = entry->size;
+ _fd.read(buffer, size);
+}
+
+Common::SeekableReadStream *NhcArchive::createStream(uint index) {
+ return createStream(&_entries[index]);
+}
+
+Common::SeekableReadStream *NhcArchive::createStream(NhcArchiveEntry *entry) {
+ return new Common::SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->size,
+ DisposeAfterUse::NO, _mutex);
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/nhcarchive.h b/engines/neverhood/nhcarchive.h
new file mode 100644
index 00000000000..a574bb3cb82
--- /dev/null
+++ b/engines/neverhood/nhcarchive.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NEVERHOOD_NHCARCHIVE_H
+#define NEVERHOOD_NHCARCHIVE_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/mutex.h"
+#include "common/stream.h"
+#include "common/substream.h"
+#include "neverhood/neverhood.h"
+
+namespace Neverhood {
+
+struct NhcArchiveEntry {
+ uint32 fileHash;
+ uint32 type;
+ uint32 offset;
+ uint32 size;
+
+ bool isNormal() const {
+ // Resources 0-10 replace resources with the same ID
+ // Resources 11 and 12 are under custom ID for subtitle font and saveload dialog messages
+ // 13 adds subtitles to a video with the same ID and so should not replace it
+ return type <= 10;
+ }
+};
+
+class NhcArchive {
+public:
+ NhcArchive() {}
+ ~NhcArchive() {}
+ bool open(const Common::String &filename, bool isOptional);
+ void load(uint index, byte *buffer, uint32 size);
+ void load(NhcArchiveEntry *entry, byte *buffer, uint32 size);
+ uint32 getSize(uint index) { return _entries[index].size; }
+ NhcArchiveEntry *getEntry(uint index) { return &_entries[index]; }
+ uint getCount() { return _entries.size(); }
+ Common::SeekableReadStream *createStream(uint index);
+ Common::SeekableReadStream *createStream(NhcArchiveEntry *entry);
+private:
+ Common::File _fd;
+ Common::Mutex _mutex;
+ Common::Array<NhcArchiveEntry> _entries;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_BLBARCHIVE_H */
diff --git a/engines/neverhood/resource.h b/engines/neverhood/resource.h
index 9fdb56975cc..ad471deedb1 100644
--- a/engines/neverhood/resource.h
+++ b/engines/neverhood/resource.h
@@ -41,6 +41,13 @@ enum {
kResTypeVideo = 10
};
+enum {
+ kResNhcTypeSubFont = 11,
+ kResNhcTypeMessages = 12,
+ kResNhcTypeSubtitles = 13
+};
+
+
class SpriteResource {
public:
SpriteResource(NeverhoodEngine *vm);
diff --git a/engines/neverhood/resourceman.cpp b/engines/neverhood/resourceman.cpp
index 76f6dc6507d..2f0394f9873 100644
--- a/engines/neverhood/resourceman.cpp
+++ b/engines/neverhood/resourceman.cpp
@@ -46,7 +46,7 @@ void ResourceMan::addArchive(const Common::String &filename, bool isOptional) {
BlbArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
if (entry) {
- if (archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
+ if (entry->archiveEntry == nullptr || archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
entry->archive = archive;
entry->archiveEntry = archiveEntry;
}
@@ -55,11 +55,39 @@ void ResourceMan::addArchive(const Common::String &filename, bool isOptional) {
newEntry.resourceHandle = -1;
newEntry.archive = archive;
newEntry.archiveEntry = archiveEntry;
+ newEntry.nhcArchive = nullptr;
+ newEntry.nhcArchiveEntry = nullptr;
_entries[archiveEntry->fileHash] = newEntry;
}
}
}
+bool ResourceMan::addNhcArchive(const Common::String &filename) {
+ NhcArchive *archive = new NhcArchive();
+ if (!archive->open(filename, true))
+ return false;
+ _nhcArchives.push_back(archive);
+ debug(3, "ResourceMan::addArchive(%s) %d files", filename.c_str(), archive->getCount());
+ for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) {
+ NhcArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
+ ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
+ if (entry) {
+ entry->nhcArchive = archive;
+ entry->nhcArchiveEntry = archiveEntry;
+ } else {
+ ResourceFileEntry newEntry;
+ newEntry.resourceHandle = -1;
+ newEntry.archive = nullptr;
+ newEntry.archiveEntry = nullptr;
+ newEntry.nhcArchive = archive;
+ newEntry.nhcArchiveEntry = archiveEntry;
+ _entries[archiveEntry->fileHash] = newEntry;
+ }
+ }
+
+ return true;
+}
+
ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) {
EntriesMap::iterator p = _entries.find(fileHash);
return p != _entries.end() ? &(*p)._value : nullptr;
@@ -69,14 +97,29 @@ ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash, ResourceFileEntry **f
ResourceFileEntry *entry = findEntrySimple(fileHash);
if (firstEntry)
*firstEntry = entry;
- for (; entry && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
+ for (; entry && entry->archiveEntry != nullptr && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
entry = findEntrySimple(fileHash);
return entry;
}
Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) {
ResourceFileEntry *entry = findEntry(fileHash);
- return entry ? entry->archive->createStream(entry->archiveEntry) : nullptr;
+ if (!entry)
+ return nullptr;
+ if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->isNormal())
+ return entry->nhcArchive->createStream(entry->nhcArchiveEntry);
+ if (entry->archiveEntry && entry->archive)
+ return entry->archive->createStream(entry->archiveEntry);
+ return nullptr;
+}
+
+Common::SeekableReadStream *ResourceMan::createNhcStream(uint32 fileHash, uint32 type) {
+ ResourceFileEntry *entry = findEntry(fileHash);
+ if (!entry)
+ return nullptr;
+ if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->type == type)
+ return entry->nhcArchive->createStream(entry->nhcArchiveEntry);
+ return nullptr;
}
void ResourceMan::queryResource(uint32 fileHash, ResourceHandle &resourceHandle) {
@@ -138,19 +181,26 @@ void ResourceMan::loadResource(ResourceHandle &resourceHandle, bool applyResourc
if (resourceData->data != nullptr) {
resourceData->dataRefCount++;
} else {
- BlbArchiveEntry *entry = resourceHandle._resourceFileEntry->archiveEntry;
-
- // Apply fixes for broken resources in Russian versions
- if (applyResourceFixes) {
- for (const EntrySizeFix *cur = entrySizeFixes; cur->fileHash > 0; ++cur) {
- if (entry->fileHash == cur->fileHash && entry->offset == cur->offset &&
- entry->diskSize == cur->diskSize && entry->size == cur->size)
- entry->size = cur->fixedSize;
+ NhcArchiveEntry *nhcEntry = resourceHandle._resourceFileEntry->nhcArchiveEntry;
+ // TODO: types B (subfont), C (MgsText), D (SubText)
+ if (nhcEntry && nhcEntry->isNormal()) {
+ resourceData->data = new byte[nhcEntry->size];
+ resourceHandle._resourceFileEntry->nhcArchive->load(nhcEntry, resourceData->data, 0);
+ } else {
+ BlbArchiveEntry *entry = resourceHandle._resourceFileEntry->archiveEntry;
+
+ // Apply fixes for broken resources in Russian versions
+ if (applyResourceFixes) {
+ for (const EntrySizeFix *cur = entrySizeFixes; cur->fileHash > 0; ++cur) {
+ if (entry->fileHash == cur->fileHash && entry->offset == cur->offset &&
+ entry->diskSize == cur->diskSize && entry->size == cur->size)
+ entry->size = cur->fixedSize;
+ }
}
- }
- resourceData->data = new byte[entry->size];
- resourceHandle._resourceFileEntry->archive->load(entry, resourceData->data, 0);
+ resourceData->data = new byte[entry->size];
+ resourceHandle._resourceFileEntry->archive->load(entry, resourceData->data, 0);
+ }
resourceData->dataRefCount = 1;
}
resourceHandle._data = resourceData->data;
diff --git a/engines/neverhood/resourceman.h b/engines/neverhood/resourceman.h
index 45ef6a928b0..deedb2507e4 100644
--- a/engines/neverhood/resourceman.h
+++ b/engines/neverhood/resourceman.h
@@ -27,6 +27,7 @@
#include "common/hashmap.h"
#include "neverhood/neverhood.h"
#include "neverhood/blbarchive.h"
+#include "neverhood/nhcarchive.h"
namespace Neverhood {
@@ -39,11 +40,14 @@ private:
BlbArchive *archive;
BlbArchiveEntry *archiveEntry;
+ NhcArchive *nhcArchive;
+ NhcArchiveEntry *nhcArchiveEntry;
+
friend class ResourceHandle;
friend class ResourceMan;
public:
- ResourceFileEntry() : resourceHandle(-1), archive(nullptr), archiveEntry(nullptr) {}
+ ResourceFileEntry() : resourceHandle(-1), archive(nullptr), archiveEntry(nullptr), nhcArchive(nullptr), nhcArchiveEntry(nullptr) {}
};
struct Resource {
@@ -62,12 +66,38 @@ friend class ResourceMan;
public:
ResourceHandle();
~ResourceHandle();
- bool isValid() const { return _resourceFileEntry != NULL && _resourceFileEntry->archiveEntry != NULL; }
- byte type() const { return isValid() ? _resourceFileEntry->archiveEntry->type : 0; };
+ bool isValid() const { return _resourceFileEntry != NULL
+ && (_resourceFileEntry->archiveEntry != NULL
+ || (_resourceFileEntry->nhcArchiveEntry != NULL && _resourceFileEntry->nhcArchiveEntry->isNormal())); }
+ byte type() const {
+ if (_resourceFileEntry == NULL)
+ return 0;
+ if (_resourceFileEntry->nhcArchiveEntry != NULL && _resourceFileEntry->nhcArchiveEntry->isNormal())
+ return _resourceFileEntry->nhcArchiveEntry->type;
+ if (_resourceFileEntry->archiveEntry != NULL)
+ return _resourceFileEntry->archiveEntry->type;
+ return 0;
+ }
const byte *data() const { return _data; }
- uint32 size() const { return isValid() ? _resourceFileEntry->archiveEntry->size : 0; };
- const byte *extData() const { return _extData; };
- uint32 fileHash() const { return isValid() ? _resourceFileEntry->archiveEntry->fileHash : 0; };
+ uint32 size() const {
+ if (_resourceFileEntry == NULL)
+ return 0;
+ if (_resourceFileEntry->nhcArchiveEntry != NULL && _resourceFileEntry->nhcArchiveEntry->isNormal())
+ return _resourceFileEntry->nhcArchiveEntry->size;
+ if (_resourceFileEntry->archiveEntry != NULL)
+ return _resourceFileEntry->archiveEntry->size;
+ return 0;
+ }
+ const byte *extData() const { return _extData; }
+ uint32 fileHash() const {
+ if (_resourceFileEntry == NULL)
+ return 0;
+ if (_resourceFileEntry->nhcArchiveEntry != NULL && _resourceFileEntry->nhcArchiveEntry->isNormal())
+ return _resourceFileEntry->nhcArchiveEntry->fileHash;
+ if (_resourceFileEntry->archiveEntry != NULL)
+ return _resourceFileEntry->archiveEntry->fileHash;
+ return 0;
+ }
protected:
ResourceFileEntry *_resourceFileEntry;
const byte *_extData;
@@ -79,9 +109,11 @@ public:
ResourceMan();
~ResourceMan();
void addArchive(const Common::String &filename, bool isOptional = false);
+ bool addNhcArchive(const Common::String &filename);
ResourceFileEntry *findEntrySimple(uint32 fileHash);
ResourceFileEntry *findEntry(uint32 fileHash, ResourceFileEntry **firstEntry = NULL);
Common::SeekableReadStream *createStream(uint32 fileHash);
+ Common::SeekableReadStream *createNhcStream(uint32 fileHash, uint32 type);
const ResourceFileEntry& getEntry(uint index) { return _entries[index]; }
uint getEntryCount() { return _entries.size(); }
void queryResource(uint32 fileHash, ResourceHandle &resourceHandle);
@@ -91,6 +123,7 @@ public:
protected:
typedef Common::HashMap<uint32, ResourceFileEntry> EntriesMap;
Common::Array<BlbArchive*> _archives;
+ Common::Array<NhcArchive*> _nhcArchives;
EntriesMap _entries;
Common::HashMap<uint32, ResourceData*> _data;
Common::Array<Resource*> _resources;
Commit: 69aa08d5314c6f1e3efba9b17637bf69831aa738
https://github.com/scummvm/scummvm/commit/69aa08d5314c6f1e3efba9b17637bf69831aa738
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Move game options from advanced options to custom widget
This will allow to add NHC drop-down.
Changed paths:
A engines/neverhood/dialogs.cpp
A engines/neverhood/dialogs.h
engines/neverhood/metaengine.cpp
engines/neverhood/module.mk
diff --git a/engines/neverhood/dialogs.cpp b/engines/neverhood/dialogs.cpp
new file mode 100644
index 00000000000..25da10a0172
--- /dev/null
+++ b/engines/neverhood/dialogs.cpp
@@ -0,0 +1,105 @@
+/* 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 "neverhood/neverhood.h"
+#include "neverhood/dialogs.h"
+
+#include "gui/gui-manager.h"
+#include "gui/message.h"
+#include "gui/saveload.h"
+#include "gui/ThemeEval.h"
+#include "gui/widget.h"
+#include "gui/widgets/popup.h"
+
+#include "common/gui_options.h"
+#include "common/system.h"
+#include "common/translation.h"
+
+namespace Neverhood {
+
+NeverhoodOptionsWidget::NeverhoodOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
+ OptionsContainerWidget(boss, name, "NeverhoodGameOptionsDialog", false, domain),
+ _originalSaveLoadCheckbox(nullptr),
+ _skipHallOfRecordsCheckbox(nullptr),
+ _scaleMakingOfVideosCheckbox(nullptr) {
+
+ _originalSaveLoadCheckbox = new GUI::CheckboxWidget(
+ widgetsBoss(),
+ "NeverhoodGameOptionsDialog.OriginalSaveLoad",
+ _("Use original save/load screens"),
+ _("Use the original save/load screens instead of the ScummVM ones"));
+
+ _skipHallOfRecordsCheckbox = new GUI::CheckboxWidget(
+ widgetsBoss(),
+ "NeverhoodGameOptionsDialog.SkipHallOfRecords", _("Skip the Hall of Records storyboard scenes"),
+ _("Allows the player to skip past the Hall of Records storyboard scenes"));
+
+ _scaleMakingOfVideosCheckbox = new GUI::CheckboxWidget(
+ widgetsBoss(),
+ "NeverhoodGameOptionsDialog.ScaleMakingOfVideos", _("Scale the making of videos to full screen"),
+ _("Scale the making of videos, so that they use the whole screen"));
+}
+
+NeverhoodOptionsWidget::~NeverhoodOptionsWidget() {
+}
+
+void NeverhoodOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
+ layouts.addDialog(layoutName, overlayedLayout)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical)
+ .addPadding(16, 16, 16, 16)
+ .addWidget("OriginalSaveLoad", "Checkbox")
+ .addWidget("SkipHallOfRecords", "Checkbox")
+ .addWidget("ScaleMakingOfVideos", "Checkbox")
+ .closeLayout()
+ .closeDialog();
+}
+
+void NeverhoodOptionsWidget::load() {
+ if (_originalSaveLoadCheckbox) {
+ _originalSaveLoadCheckbox->setState(ConfMan.getBool("originalsaveload", _domain));
+ }
+
+ if (_skipHallOfRecordsCheckbox) {
+ _skipHallOfRecordsCheckbox->setState(ConfMan.getBool("skiphallofrecordsscenes", _domain));
+ }
+
+ if (_scaleMakingOfVideosCheckbox) {
+ _scaleMakingOfVideosCheckbox->setState(ConfMan.getBool("scalemakingofvideos", _domain));
+ }
+}
+
+bool NeverhoodOptionsWidget::save() {
+ if (_originalSaveLoadCheckbox) {
+ ConfMan.setBool("originalsaveload", _originalSaveLoadCheckbox->getState(), _domain);
+ }
+
+ if (_skipHallOfRecordsCheckbox) {
+ ConfMan.setBool("skiphallofrecordsscenes", _skipHallOfRecordsCheckbox->getState(), _domain);
+ }
+
+ if (_scaleMakingOfVideosCheckbox) {
+ ConfMan.setBool("scalemakingofvideos", _scaleMakingOfVideosCheckbox->getState(), _domain);
+ }
+
+ return true;
+}
+
+} // End of namespace Neverhood
diff --git a/engines/neverhood/dialogs.h b/engines/neverhood/dialogs.h
new file mode 100644
index 00000000000..ebcae285a30
--- /dev/null
+++ b/engines/neverhood/dialogs.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NEVERHOOD_DIALOGS_H
+#define NEVERHOOD_DIALOGS_H
+
+#include "neverhood/neverhood.h"
+
+#include "common/events.h"
+#include "common/str.h"
+#include "common/ustr.h"
+
+#include "engines/dialogs.h"
+
+#include "gui/dialog.h"
+#include "gui/widget.h"
+
+namespace GUI {
+class PopUpWidget;
+}
+
+namespace Neverhood {
+
+class NeverhoodEngine;
+
+class NeverhoodOptionsWidget : public GUI::OptionsContainerWidget {
+public:
+ NeverhoodOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
+ ~NeverhoodOptionsWidget() override;
+
+ // OptionsContainerWidget API
+ void load() override;
+ bool save() override;
+
+private:
+ // OptionsContainerWidget API
+ void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
+
+ GUI::CheckboxWidget *_originalSaveLoadCheckbox;
+ GUI::CheckboxWidget *_skipHallOfRecordsCheckbox;
+ GUI::CheckboxWidget *_scaleMakingOfVideosCheckbox;
+};
+
+} // End of namespace Neverhood
+
+#endif
diff --git a/engines/neverhood/metaengine.cpp b/engines/neverhood/metaengine.cpp
index 060f805e7a1..1a9217fda19 100644
--- a/engines/neverhood/metaengine.cpp
+++ b/engines/neverhood/metaengine.cpp
@@ -25,48 +25,12 @@
#include "common/file.h"
#include "common/translation.h"
+#include "neverhood/dialogs.h"
#include "neverhood/neverhood.h"
#include "neverhood/detection.h"
namespace Neverhood {
-static const ADExtraGuiOptionsMap optionsList[] = {
- {
- GAMEOPTION_ORIGINAL_SAVELOAD,
- {
- _s("Use original save/load screens"),
- _s("Use the original save/load screens instead of the ScummVM ones"),
- "originalsaveload",
- false,
- 0,
- 0
- }
- },
- {
- GAMEOPTION_SKIP_HALL_OF_RECORDS,
- {
- _s("Skip the Hall of Records storyboard scenes"),
- _s("Allows the player to skip past the Hall of Records storyboard scenes"),
- "skiphallofrecordsscenes",
- false,
- 0,
- 0
- }
- },
- {
- GAMEOPTION_SCALE_MAKING_OF_VIDEOS,
- {
- _s("Scale the making of videos to full screen"),
- _s("Scale the making of videos, so that they use the whole screen"),
- "scalemakingofvideos",
- false,
- 0,
- 0
- }
- },
- AD_EXTRA_GUI_OPTIONS_TERMINATOR
-};
-
const char *NeverhoodEngine::getGameId() const {
return _gameDescription->gameId;
}
@@ -99,8 +63,8 @@ public:
return "neverhood";
}
- const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
- return Neverhood::optionsList;
+ GUI::OptionsContainerWidget *buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
+ return new Neverhood::NeverhoodOptionsWidget(boss, name, target);
}
bool hasFeature(MetaEngineFeature f) const override;
diff --git a/engines/neverhood/module.mk b/engines/neverhood/module.mk
index e7b7e97167e..c396d8f6aa9 100644
--- a/engines/neverhood/module.mk
+++ b/engines/neverhood/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS = \
background.o \
blbarchive.o \
console.o \
+ dialogs.o \
diskplayerscene.o \
entity.o \
gamemodule.o \
Commit: c0455314c97e70aa0c6f7ef5a7d06844a9198766
https://github.com/scummvm/scummvm/commit/c0455314c97e70aa0c6f7ef5a7d06844a9198766
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add NHC selection item
Changed paths:
engines/neverhood/dialogs.cpp
engines/neverhood/dialogs.h
diff --git a/engines/neverhood/dialogs.cpp b/engines/neverhood/dialogs.cpp
index 25da10a0172..b458fbe2250 100644
--- a/engines/neverhood/dialogs.cpp
+++ b/engines/neverhood/dialogs.cpp
@@ -39,7 +39,8 @@ NeverhoodOptionsWidget::NeverhoodOptionsWidget(GuiObject *boss, const Common::St
OptionsContainerWidget(boss, name, "NeverhoodGameOptionsDialog", false, domain),
_originalSaveLoadCheckbox(nullptr),
_skipHallOfRecordsCheckbox(nullptr),
- _scaleMakingOfVideosCheckbox(nullptr) {
+ _scaleMakingOfVideosCheckbox(nullptr),
+ _nhcPopUp(nullptr) {
_originalSaveLoadCheckbox = new GUI::CheckboxWidget(
widgetsBoss(),
@@ -56,6 +57,31 @@ NeverhoodOptionsWidget::NeverhoodOptionsWidget(GuiObject *boss, const Common::St
widgetsBoss(),
"NeverhoodGameOptionsDialog.ScaleMakingOfVideos", _("Scale the making of videos to full screen"),
_("Scale the making of videos, so that they use the whole screen"));
+
+ Common::String path = ConfMan.get("path", _domain);
+ Common::FSDirectory dir(path);
+ Common::FSDirectory *langdir = dir.getSubDirectory("language");
+ _nhcFiles.push_back("");
+ if (langdir) {
+ Common::ArchiveMemberList nhcFileList;
+ langdir->listMatchingMembers(nhcFileList, "*.nhc");
+
+ for (Common::ArchiveMemberList::iterator iter = nhcFileList.begin(); iter != nhcFileList.end(); ++iter) {
+ Common::String nhcFileName = (*iter)->getName();
+ nhcFileName.erase(nhcFileName.size() - 4); // remove .nhc extension
+ _nhcFiles.push_back(nhcFileName);
+ }
+ }
+
+ if (_nhcFiles.size() > 1) {
+ GUI::StaticTextWidget *nhcCaption = new GUI::StaticTextWidget(widgetsBoss(), "NeverhoodGameOptionsDialog.NhcDesc", _("NHC replacement:"));
+ nhcCaption->setAlign(Graphics::kTextAlignRight);
+
+ _nhcPopUp = new GUI::PopUpWidget(widgetsBoss(), "NeverhoodGameOptionsDialog.Nhc");
+
+ for (uint i = 0; i < _nhcFiles.size(); i++)
+ _nhcPopUp->appendEntry(_nhcFiles[i].empty() ? _("<original>") : Common::U32String(_nhcFiles[i]), i);
+ }
}
NeverhoodOptionsWidget::~NeverhoodOptionsWidget() {
@@ -68,6 +94,11 @@ void NeverhoodOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common:
.addWidget("OriginalSaveLoad", "Checkbox")
.addWidget("SkipHallOfRecords", "Checkbox")
.addWidget("ScaleMakingOfVideos", "Checkbox")
+ .addLayout(GUI::ThemeLayout::kLayoutHorizontal)
+ .addPadding(0, 0, 0, 0)
+ .addWidget("NhcDesc", "OptionsLabel")
+ .addWidget("Nhc", "PopUp")
+ .closeLayout()
.closeLayout()
.closeDialog();
}
@@ -84,6 +115,13 @@ void NeverhoodOptionsWidget::load() {
if (_scaleMakingOfVideosCheckbox) {
_scaleMakingOfVideosCheckbox->setState(ConfMan.getBool("scalemakingofvideos", _domain));
}
+
+ if (_nhcPopUp) {
+ Common::String nhcFile(ConfMan.get("nhc_file", _domain));
+ for (uint i = 0; i < _nhcFiles.size(); i++)
+ if (_nhcFiles[i].equalsIgnoreCase(nhcFile))
+ _nhcPopUp->setSelectedTag(i);
+ }
}
bool NeverhoodOptionsWidget::save() {
@@ -99,6 +137,12 @@ bool NeverhoodOptionsWidget::save() {
ConfMan.setBool("scalemakingofvideos", _scaleMakingOfVideosCheckbox->getState(), _domain);
}
+ if (_nhcPopUp) {
+ uint32 selectedNhcFile = _nhcPopUp->getSelectedTag();
+ if (selectedNhcFile < _nhcFiles.size())
+ ConfMan.set("nhc_file", _nhcFiles[selectedNhcFile], _domain);
+ }
+
return true;
}
diff --git a/engines/neverhood/dialogs.h b/engines/neverhood/dialogs.h
index ebcae285a30..0c683e6d5ba 100644
--- a/engines/neverhood/dialogs.h
+++ b/engines/neverhood/dialogs.h
@@ -57,6 +57,9 @@ private:
GUI::CheckboxWidget *_originalSaveLoadCheckbox;
GUI::CheckboxWidget *_skipHallOfRecordsCheckbox;
GUI::CheckboxWidget *_scaleMakingOfVideosCheckbox;
+
+ GUI::PopUpWidget *_nhcPopUp;
+ Common::StringArray _nhcFiles;
};
} // End of namespace Neverhood
Commit: 103bb43ab252b7d98c31be36c3a9f49fd3542d8a
https://github.com/scummvm/scummvm/commit/103bb43ab252b7d98c31be36c3a9f49fd3542d8a
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Load nhc file
Changed paths:
engines/neverhood/neverhood.cpp
diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp
index eb89bf80e96..0cffb91bffc 100644
--- a/engines/neverhood/neverhood.cpp
+++ b/engines/neverhood/neverhood.cpp
@@ -100,6 +100,10 @@ Common::Error NeverhoodEngine::run() {
_res->addArchive("t.blb");
}
+ Common::String nhcFile = ConfMan.get("nhc_file");
+ if (!nhcFile.empty())
+ _res->addNhcArchive("language/" + nhcFile + ".nhc");
+
CursorMan.showMouse(false);
_soundMan = new SoundMan(this);
Commit: 031c9296811d2484f3ce296dfdb1595c6d9c9253
https://github.com/scummvm/scummvm/commit/031c9296811d2484f3ce296dfdb1595c6d9c9253
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add loading of subtitle font
Changed paths:
engines/neverhood/neverhood.cpp
engines/neverhood/neverhood.h
diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp
index 0cffb91bffc..e7f53b91552 100644
--- a/engines/neverhood/neverhood.cpp
+++ b/engines/neverhood/neverhood.cpp
@@ -47,7 +47,7 @@
namespace Neverhood {
NeverhoodEngine::NeverhoodEngine(OSystem *syst, const ADGameDescription *gameDesc) :
- Engine(syst), _gameDescription(gameDesc) {
+ Engine(syst), _gameDescription(gameDesc), _haveSubtitles(false) {
// Setup mixer
if (!_mixer->isReady()) {
warning("Sound initialization failed.");
@@ -101,8 +101,23 @@ Common::Error NeverhoodEngine::run() {
}
Common::String nhcFile = ConfMan.get("nhc_file");
- if (!nhcFile.empty())
- _res->addNhcArchive("language/" + nhcFile + ".nhc");
+ if (!nhcFile.empty() && _res->addNhcArchive("language/" + nhcFile + ".nhc")) {
+ Common::SeekableReadStream *s = _res->createNhcStream(0x544E4F46, kResNhcTypeSubFont);
+ if (s && s->size() >= 4096) {
+ for (uint i = 0; i < 256; i++) {
+ s->read(&_subFont[i].bitmap, sizeof(_subFont[i].bitmap));
+ for (uint j = 0; j < 16; j++)
+ _subFont[i].outline[j] = (_subFont[i].bitmap[j] << 1) | (_subFont[i].bitmap[j] >> 1);
+ for (uint j = 1; j < 16; j++)
+ _subFont[i].outline[j] |= _subFont[i].bitmap[j-1];
+ for (uint j = 0; j < 15; j++)
+ _subFont[i].outline[j] |= _subFont[i].bitmap[j+1];
+ for (uint j = 0; j < 16; j++)
+ _subFont[i].outline[j] &= ~_subFont[i].bitmap[j];
+ }
+ _haveSubtitles = true;
+ }
+ }
CursorMan.showMouse(false);
diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h
index c370153ca4c..5b13eefe831 100644
--- a/engines/neverhood/neverhood.h
+++ b/engines/neverhood/neverhood.h
@@ -52,6 +52,11 @@ struct GameState {
int which;
};
+struct SubtitleGlyph {
+ byte bitmap[16];
+ byte outline[16];
+};
+
class NeverhoodEngine : public ::Engine {
protected:
@@ -134,10 +139,16 @@ public:
void toggleMusic(bool state) { _enableMusic = state; }
bool musicIsEnabled() { return _enableMusic; }
+ const SubtitleGlyph *getSubfont() const {
+ return _haveSubtitles ? _subFont : nullptr;
+ }
+
private:
bool _updateSound;
bool _enableMusic;
+ SubtitleGlyph _subFont[256];
+ bool _haveSubtitles;
};
} // End of namespace Neverhood
Commit: 97b500643e6760e9e2c30c20dbc3c3ab1424f112
https://github.com/scummvm/scummvm/commit/97b500643e6760e9e2c30c20dbc3c3ab1424f112
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add subtitle player
Changed paths:
A engines/neverhood/subtitles.cpp
A engines/neverhood/subtitles.h
engines/neverhood/module.mk
diff --git a/engines/neverhood/module.mk b/engines/neverhood/module.mk
index c396d8f6aa9..6f27485eaa1 100644
--- a/engines/neverhood/module.mk
+++ b/engines/neverhood/module.mk
@@ -68,7 +68,8 @@ MODULE_OBJS = \
smackerplayer.o \
sound.o \
sprite.o \
- staticdata.o
+ staticdata.o \
+ subtitles.o
# This module can be built as a plugin
ifeq ($(ENABLE_NEVERHOOD), DYNAMIC_PLUGIN)
diff --git a/engines/neverhood/subtitles.cpp b/engines/neverhood/subtitles.cpp
new file mode 100644
index 00000000000..b94b41e1863
--- /dev/null
+++ b/engines/neverhood/subtitles.cpp
@@ -0,0 +1,120 @@
+/* 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 "neverhood/resource.h"
+#include "neverhood/resourceman.h"
+#include "neverhood/subtitles.h"
+
+namespace Neverhood {
+
+namespace {
+void drawSubtitles(Graphics::Surface *surf, const Common::String &str, const SubtitleGlyph *subfont, int x0) {
+ if (!surf || surf->h < SubtitlePlayer::kSubtitleCharHeight || !subfont)
+ return;
+
+ byte *dest0 = (byte*)surf->getBasePtr(0, 0);
+
+ int lastx = MIN<int>(str.size() * SubtitlePlayer::kSubtitleCharWidth + x0, surf->w);
+ for (int16 yc = 0; yc < SubtitlePlayer::kSubtitleCharHeight; yc++) {
+ byte *dest = dest0 + yc * surf->pitch;
+ memset(dest, SubtitlePlayer::kSubtitleAlpha, x0);
+ memset(dest + lastx, SubtitlePlayer::kSubtitleAlpha, surf->w - lastx);
+ }
+
+ for (int i = 0; i < (int)str.size() && i * SubtitlePlayer::kSubtitleCharWidth < surf->w; i++) {
+ byte c = str[i];
+ byte *dest = dest0 + i * SubtitlePlayer::kSubtitleCharWidth + x0;
+ for (int16 yc = 0; yc < SubtitlePlayer::kSubtitleCharHeight; yc++) {
+ byte *row = dest;
+ for (int16 xc = 0; xc < SubtitlePlayer::kSubtitleCharWidth; xc++, row++) {
+ if ((subfont[c].bitmap[yc] << xc) & 0x80)
+ *row = 0xff;
+ else if ((subfont[c].outline[yc] << xc) & 0x80)
+ *row = 0x00;
+ else
+ *row = SubtitlePlayer::kSubtitleAlpha;
+ }
+ dest += surf->pitch;
+ }
+ }
+}
+}
+
+SubtitlePlayer::SubtitlePlayer(NeverhoodEngine *vm, uint32 fileHash, int width) :
+ _vm(vm), _haveBottomSubs(false), _haveTopSubs(false), _currentFrame(-1), _isValid(false) {
+ if (!vm->getSubfont())
+ return;
+ _isValid = true;
+ _bottomSubs.create(width, kSubtitleCharHeight, Graphics::PixelFormat::createFormatCLUT8());
+ _topSubs.create(width, kSubtitleCharHeight, Graphics::PixelFormat::createFormatCLUT8());
+
+ Common::SeekableReadStream *s = vm->_res->createNhcStream(fileHash, kResNhcTypeSubtitles);
+ while (s && !s->eos()) {
+ Subtitle sub;
+ sub.fromFrame = s->readUint32LE();
+ sub.toFrame = s->readUint32LE();
+ sub.text = s->readString('\0', 40);
+ if (!sub.text.empty() && sub.text[0] == '^') {
+ sub.isTop = true;
+ sub.text = sub.text.substr(1);
+ } else {
+ sub.isTop = false;
+ }
+ _subtitles.push_back(sub);
+ }
+ delete s;
+}
+
+void SubtitlePlayer::renderFrame(uint frameNumber, int centerX) {
+ // Reuse old rendering if no frame has passed
+ if (_currentFrame == (int64)frameNumber)
+ return;
+
+ const SubtitleGlyph *subFont = _vm->getSubfont();
+ if (!subFont)
+ return;
+
+ int screenWidth = _bottomSubs.w;
+
+ _haveBottomSubs = false;
+ _haveTopSubs = false;
+
+ // TODO: Optimize this
+ for (uint i = 0; i < _subtitles.size(); i++) {
+ if (frameNumber < _subtitles[i].fromFrame || frameNumber > _subtitles[i].toFrame)
+ continue;
+ Common::String curStr = _subtitles[i].text;
+ if ((int)curStr.size() > screenWidth / SubtitlePlayer::kSubtitleCharWidth)
+ curStr = curStr.substr(0, screenWidth / SubtitlePlayer::kSubtitleCharWidth - 3) + "...";
+ int width = curStr.size() * SubtitlePlayer::kSubtitleCharWidth;
+ int startX = MAX(MIN(centerX - width / 2, screenWidth - width), 0);
+
+ if (_subtitles[i].isTop) {
+ drawSubtitles(&_topSubs, curStr, subFont, startX);
+ _haveTopSubs = true;
+ } else {
+ drawSubtitles(&_bottomSubs, curStr, subFont, startX);
+ _haveBottomSubs = true;
+ }
+ }
+}
+
+}
diff --git a/engines/neverhood/subtitles.h b/engines/neverhood/subtitles.h
new file mode 100644
index 00000000000..3cd4d656aee
--- /dev/null
+++ b/engines/neverhood/subtitles.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NEVERHOOD_SUBTITLES_H
+#define NEVERHOOD_SUBTITLES_H
+
+#include "common/str.h"
+#include "neverhood/neverhood.h"
+#include "graphics/surface.h"
+
+namespace Neverhood {
+
+struct Subtitle {
+ uint32 fromFrame;
+ uint32 toFrame;
+ Common::String text;
+ bool isTop;
+};
+
+class SubtitlePlayer {
+public:
+ SubtitlePlayer(NeverhoodEngine *vm, uint32 fileHash, int width);
+
+ void renderFrame(uint frameNumber, int centerX);
+ const Graphics::Surface *getBottomSubs() const { return _haveBottomSubs ? &_bottomSubs : nullptr; }
+ const Graphics::Surface *getTopSubs() const { return _haveTopSubs ? &_topSubs : nullptr; }
+ bool isValid() const { return _isValid && !_subtitles.empty(); }
+
+ static const byte kSubtitleAlpha = 0x77;
+ static const int kSubtitleCharHeight = 16;
+ static const int kSubtitleCharWidth = 8;
+
+private:
+ NeverhoodEngine *_vm;
+ bool _isValid;
+ Common::Array<Subtitle> _subtitles;
+ Graphics::Surface _bottomSubs;
+ Graphics::Surface _topSubs;
+ bool _haveBottomSubs;
+ bool _haveTopSubs;
+ int64 _currentFrame;
+};
+
+} // End of namespace Neverhood
+
+#endif /* NEVERHOOD_SUBTITLES_H */
Commit: feb1d77ec723faa80110fe4b0ce25ca0d45eae0a
https://github.com/scummvm/scummvm/commit/feb1d77ec723faa80110fe4b0ce25ca0d45eae0a
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Show subtitles in smackerplayer
Changed paths:
engines/neverhood/smackerplayer.cpp
engines/neverhood/smackerplayer.h
diff --git a/engines/neverhood/smackerplayer.cpp b/engines/neverhood/smackerplayer.cpp
index 4b158aa4809..2f460854f02 100644
--- a/engines/neverhood/smackerplayer.cpp
+++ b/engines/neverhood/smackerplayer.cpp
@@ -35,8 +35,30 @@ SmackerSurface::SmackerSurface(NeverhoodEngine *vm)
}
void SmackerSurface::draw() {
- if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
+ if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0) {
_vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false, ++_version);
+ if (_subtitles && _subtitles->isValid()) {
+ _subtitles->renderFrame(frameNumber, 160);
+ const Graphics::Surface *bottom = _subtitles->getBottomSubs();
+ if (bottom) {
+ NDrawRect subDrawRect;
+ subDrawRect.x = _drawRect.x;
+ subDrawRect.y = _drawRect.y + _drawRect.height - 17;
+ subDrawRect.width = _drawRect.width;
+ subDrawRect.height = 16;
+ _vm->_screen->drawSurface2(bottom, subDrawRect, _clipRect, true, ++_version, nullptr, _subtitles->kSubtitleAlpha);
+ }
+ const Graphics::Surface *top = _subtitles->getTopSubs();
+ if (top) {
+ NDrawRect subDrawRect;
+ subDrawRect.x = _drawRect.x;
+ subDrawRect.y = _drawRect.y + 1;
+ subDrawRect.width = _drawRect.width;
+ subDrawRect.height = 16;
+ _vm->_screen->drawSurface2(top, subDrawRect, _clipRect, true, ++_version, nullptr, _subtitles->kSubtitleAlpha);
+ }
+ }
+ }
}
void SmackerSurface::setSmackerFrame(const Graphics::Surface *smackerFrame) {
@@ -70,8 +92,30 @@ SmackerDoubleSurface::SmackerDoubleSurface(NeverhoodEngine *vm)
}
void SmackerDoubleSurface::draw() {
- if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
+ if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0) {
_vm->_screen->drawDoubleSurface2(_smackerFrame, _drawRect);
+ if (_subtitles && _subtitles->isValid()) {
+ _subtitles->renderFrame(frameNumber, 160);
+ const Graphics::Surface *bottom = _subtitles->getBottomSubs();
+ if (bottom) {
+ NDrawRect subDrawRect;
+ subDrawRect.x = _drawRect.x;
+ subDrawRect.y = _drawRect.y + _drawRect.height * 2 - 34;
+ subDrawRect.width = _drawRect.width;
+ subDrawRect.height = 16;
+ _vm->_screen->drawDoubleSurface2Alpha(bottom, subDrawRect, _subtitles->kSubtitleAlpha);
+ }
+ const Graphics::Surface *top = _subtitles->getTopSubs();
+ if (top) {
+ NDrawRect subDrawRect;
+ subDrawRect.x = _drawRect.x;
+ subDrawRect.y = _drawRect.y + 2;
+ subDrawRect.width = _drawRect.width;
+ subDrawRect.height = 16;
+ _vm->_screen->drawDoubleSurface2Alpha(top, subDrawRect, _subtitles->kSubtitleAlpha);
+ }
+ }
+ }
}
// NeverhoodSmackerDecoder
@@ -134,6 +178,8 @@ void SmackerPlayer::open(uint32 fileHash, bool keepLastFrame) {
_stream = _vm->_res->createStream(fileHash);
+ _smackerSurface->_subtitles.reset(new SubtitlePlayer(_vm, fileHash, 320));
+
_smackerDecoder = new NeverhoodSmackerDecoder();
_smackerDecoder->loadStream(_stream);
@@ -222,6 +268,8 @@ void SmackerPlayer::updateFrame() {
const Graphics::Surface *smackerFrame = _smackerDecoder->decodeNextFrame();
+ _smackerSurface->frameNumber = _smackerDecoder->getCurFrame();
+
if (_smackerFirst) {
_smackerSurface->setSmackerFrame(smackerFrame);
if (_drawX < 0 || _drawY < 0) {
diff --git a/engines/neverhood/smackerplayer.h b/engines/neverhood/smackerplayer.h
index c2010a2974b..24dd4e7c8bd 100644
--- a/engines/neverhood/smackerplayer.h
+++ b/engines/neverhood/smackerplayer.h
@@ -25,11 +25,13 @@
#include "video/smk_decoder.h"
#include "neverhood/neverhood.h"
#include "neverhood/entity.h"
+#include "neverhood/subtitles.h"
namespace Neverhood {
class Scene;
class Palette;
+class SmackerPlayer;
class SmackerSurface : public BaseSurface {
public:
@@ -37,8 +39,12 @@ public:
void draw() override;
void setSmackerFrame(const Graphics::Surface *smackerFrame);
void unsetSmackerFrame();
+ void renderSubtitles();
protected:
const Graphics::Surface *_smackerFrame;
+ Common::ScopedPtr<SubtitlePlayer> _subtitles;
+ uint32 frameNumber;
+ friend class SmackerPlayer;
};
class SmackerDoubleSurface : public SmackerSurface {
Commit: 03a59a295380d3ad6888c2e806090e35241101e5
https://github.com/scummvm/scummvm/commit/03a59a295380d3ad6888c2e806090e35241101e5
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Support blitting with alphaColor != 0
Changed paths:
engines/neverhood/screen.cpp
engines/neverhood/screen.h
diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp
index 1e60499dcd5..1defb192211 100644
--- a/engines/neverhood/screen.cpp
+++ b/engines/neverhood/screen.cpp
@@ -186,7 +186,7 @@ void Screen::clearRenderQueue() {
}
void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version,
- const Graphics::Surface *shadowSurface) {
+ const Graphics::Surface *shadowSurface, byte alphaColor) {
int16 destX, destY;
NRect ddRect;
@@ -217,7 +217,7 @@ void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect,
ddRect.y1 = 0;
}
- queueBlit(surface, destX, destY, ddRect, transparent, version, shadowSurface);
+ queueBlit(surface, destX, destY, ddRect, transparent, version, shadowSurface, alphaColor);
}
@@ -350,7 +350,7 @@ void Screen::drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &d
}
void Screen::queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version,
- const Graphics::Surface *shadowSurface) {
+ const Graphics::Surface *shadowSurface, byte alphaColor) {
const int width = ddRect.x2 - ddRect.x1;
const int height = ddRect.y2 - ddRect.y1;
@@ -369,6 +369,7 @@ void Screen::queueBlit(const Graphics::Surface *surface, int16 destX, int16 dest
renderItem._height = height;
renderItem._transparent = transparent;
renderItem._version = version;
+ renderItem._alphaColor = alphaColor;
_renderQueue->push_back(renderItem);
}
@@ -406,7 +407,7 @@ void Screen::blitRenderItem(const RenderItem &renderItem, const Common::Rect &cl
source += surface->pitch;
dest += _backScreen->pitch;
}
- } else {
+ } else if (renderItem._alphaColor == 0) {
while (height--) {
for (int xc = 0; xc < width; xc++)
if (source[xc] != 0)
@@ -414,6 +415,14 @@ void Screen::blitRenderItem(const RenderItem &renderItem, const Common::Rect &cl
source += surface->pitch;
dest += _backScreen->pitch;
}
+ } else {
+ while (height--) {
+ for (int xc = 0; xc < width; xc++)
+ if (source[xc] != renderItem._alphaColor)
+ dest[xc] = source[xc];
+ source += surface->pitch;
+ dest += _backScreen->pitch;
+ }
}
}
diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h
index b52dd9a9df9..31c899f70bf 100644
--- a/engines/neverhood/screen.h
+++ b/engines/neverhood/screen.h
@@ -42,6 +42,7 @@ struct RenderItem {
bool _transparent;
byte _version;
bool _refresh;
+ byte _alphaColor;
bool operator==(const RenderItem &second) const {
return
_surface == second._surface &&
@@ -53,7 +54,8 @@ struct RenderItem {
_width == second._width &&
_height == second._height &&
_transparent == second._transparent &&
- _version == second._version;
+ _version == second._version &&
+ _alphaColor == second._alphaColor;
}
};
@@ -79,14 +81,14 @@ public:
void clear();
void clearRenderQueue();
void drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version,
- const Graphics::Surface *shadowSurface = NULL);
+ const Graphics::Surface *shadowSurface = NULL, byte alphaColor = 0);
void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version);
void drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect);
void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version);
void drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version);
void setSmackerDecoder(Video::SmackerDecoder *smackerDecoder) { _smackerDecoder = smackerDecoder; }
void queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version,
- const Graphics::Surface *shadowSurface = NULL);
+ const Graphics::Surface *shadowSurface = NULL, byte alphaColor = 0);
void blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect);
protected:
NeverhoodEngine *_vm;
Commit: f4da2d054a8fea2d32fd4fa82499985fa30bc74a
https://github.com/scummvm/scummvm/commit/f4da2d054a8fea2d32fd4fa82499985fa30bc74a
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add drawDoubleSurface2Alpha
Changed paths:
engines/neverhood/screen.cpp
engines/neverhood/screen.h
diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp
index 1defb192211..fc0d7879aec 100644
--- a/engines/neverhood/screen.cpp
+++ b/engines/neverhood/screen.cpp
@@ -276,6 +276,31 @@ void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &dra
}
+void Screen::drawDoubleSurface2Alpha(const Graphics::Surface *surface, NDrawRect &drawRect, byte alphaColor) {
+
+ const byte *source = (const byte*)surface->getPixels();
+ byte *dest = (byte*)_backScreen->getBasePtr(drawRect.x, drawRect.y);
+
+ for (int16 yc = 0; yc < surface->h; yc++) {
+ byte *row = dest;
+ for (int16 xc = 0; xc < surface->w; xc++) {
+ if (*source != alphaColor) {
+ row[0] = *source;
+ row[1] = *source;
+ row[_backScreen->pitch] = *source;
+ row[_backScreen->pitch + 1] = *source;
+ }
+ source++;
+ row += 2;
+ }
+ dest += _backScreen->pitch;
+ dest += _backScreen->pitch;
+ }
+
+ _fullRefresh = true; // See Screen::update
+
+}
+
void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version) {
int16 x, y;
diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h
index 31c899f70bf..7bd2f3b4fc5 100644
--- a/engines/neverhood/screen.h
+++ b/engines/neverhood/screen.h
@@ -84,6 +84,7 @@ public:
const Graphics::Surface *shadowSurface = NULL, byte alphaColor = 0);
void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version);
void drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect);
+ void drawDoubleSurface2Alpha(const Graphics::Surface *surface, NDrawRect &drawRect, byte alphaColor);
void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version);
void drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version);
void setSmackerDecoder(Video::SmackerDecoder *smackerDecoder) { _smackerDecoder = smackerDecoder; }
Commit: ffdf90df45f0213ee45e63af7fed4a65fdfbd5e0
https://github.com/scummvm/scummvm/commit/ffdf90df45f0213ee45e63af7fed4a65fdfbd5e0
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Allocate more space for font surface
NHC-based Russian translation has extra unused space in the sprite.
This causes the code to skip drawing sprite on the surface altogether.
The simplest solution is to simply allocate a bit more with only
downside is a small increase in memory footprint
Changed paths:
engines/neverhood/graphics.cpp
diff --git a/engines/neverhood/graphics.cpp b/engines/neverhood/graphics.cpp
index 135007c144d..5fda85f155c 100644
--- a/engines/neverhood/graphics.cpp
+++ b/engines/neverhood/graphics.cpp
@@ -151,7 +151,7 @@ void ShadowSurface::draw() {
// FontSurface
FontSurface::FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight)
- : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
+ : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows + 4, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
_firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(nullptr) {
_tracking = new NPointArray();
@@ -160,7 +160,7 @@ FontSurface::FontSurface(NeverhoodEngine *vm, NPointArray *tracking, uint charsP
}
FontSurface::FontSurface(NeverhoodEngine *vm, uint32 fileHash, uint charsPerRow, uint16 numRows, byte firstChar, uint16 charWidth, uint16 charHeight)
- : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
+ : BaseSurface(vm, 0, charWidth * charsPerRow, charHeight * numRows + 4, "font"), _charsPerRow(charsPerRow), _numRows(numRows),
_firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _tracking(nullptr) {
SpriteResource fontSpriteResource(_vm);
Commit: 5d6ba180088b7b710ac7f7d9ecd81f0c8dbe1077
https://github.com/scummvm/scummvm/commit/5d6ba180088b7b710ac7f7d9ecd81f0c8dbe1077
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Support subtitles for sprites
Changed paths:
engines/neverhood/scene.cpp
engines/neverhood/sprite.cpp
engines/neverhood/sprite.h
diff --git a/engines/neverhood/scene.cpp b/engines/neverhood/scene.cpp
index 7cf526a8d64..d79f1187572 100644
--- a/engines/neverhood/scene.cpp
+++ b/engines/neverhood/scene.cpp
@@ -158,6 +158,7 @@ void Scene::printSurfaces(Console *con) {
Sprite *Scene::addSprite(Sprite *sprite) {
addEntity(sprite);
addSurface(sprite->getSurface());
+ addSurface(sprite->getSubtitleSurface());
return sprite;
}
diff --git a/engines/neverhood/sprite.cpp b/engines/neverhood/sprite.cpp
index 4685038026f..808c3a953f3 100644
--- a/engines/neverhood/sprite.cpp
+++ b/engines/neverhood/sprite.cpp
@@ -187,13 +187,13 @@ void StaticSprite::updatePosition() {
// AnimatedSprite
AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, int objectPriority)
- : Sprite(vm, objectPriority), _animResource(vm) {
+ : Sprite(vm, objectPriority), _animResource(vm), _subtitleSurface(vm, this) {
init();
}
AnimatedSprite::AnimatedSprite(NeverhoodEngine *vm, uint32 fileHash, int surfacePriority, int16 x, int16 y)
- : Sprite(vm, 1100), _animResource(vm) {
+ : Sprite(vm, 1100), _animResource(vm), _subtitleSurface(vm, this) {
init();
SetUpdateHandler(&AnimatedSprite::update);
@@ -313,6 +313,7 @@ void AnimatedSprite::updateAnim() {
if (_animStatus == 1) {
if (_animResource.load(_newAnimFileHash)) {
_currAnimFileHash = _newAnimFileHash;
+ _subtitles.reset(new SubtitlePlayer(_vm, _newAnimFileHash, kSubtitleWidth));
} else {
_animResource.load(calcHash("sqDefault"));
_currAnimFileHash = 0;
@@ -326,6 +327,7 @@ void AnimatedSprite::updateAnim() {
} else {
if (_animResource.load(_newAnimFileHash)) {
_currAnimFileHash = _newAnimFileHash;
+ _subtitles.reset(new SubtitlePlayer(_vm, _newAnimFileHash, kSubtitleWidth));
} else {
_animResource.load(calcHash("sqDefault"));
_currAnimFileHash = 0;
@@ -369,6 +371,12 @@ void AnimatedSprite::updatePosition() {
_surface->getDrawRect().y = filterY(_y + _drawOffset.y);
}
+ int subCenterX = _surface->getDrawRect().x + _surface->getDrawRect().width / 2;
+ _subtitleSurface.getDrawRect().x = MAX(subCenterX - kSubtitleWidth / 2, 0);
+ _subtitleSurface.getDrawRect().width = kSubtitleWidth;
+ _subtitleSurface.getDrawRect().y = MIN(_surface->getDrawRect().y + _surface->getDrawRect().height + 1, 480 - (SubtitlePlayer::kSubtitleCharHeight - 1));
+ _subtitleSurface.getDrawRect().height = SubtitlePlayer::kSubtitleCharHeight;
+
if (_needRefresh) {
_surface->drawAnimResource(_animResource, _currFrameIndex, _doDeltaX, _doDeltaY, _drawOffset.width, _drawOffset.height);
_needRefresh = false;
@@ -398,6 +406,25 @@ void AnimatedSprite::updateFrameIndex() {
}
}
+AnimatedSprite::AnimatedSpriteSubtitles::AnimatedSpriteSubtitles(NeverhoodEngine *vm, AnimatedSprite *backref) :
+ _backref(backref), BaseSurface(vm, 0xffff, kSubtitleWidth, SubtitlePlayer::kSubtitleCharHeight, "animated sprite subtitles") {
+}
+
+void AnimatedSprite::AnimatedSpriteSubtitles::draw() {
+ if (_backref->_subtitles && _backref->_subtitles->isValid() &&
+ _backref->_currFrameIndex != 0 &&
+ _backref->_currFrameIndex < _backref->_lastFrameIndex) {
+ int subCenterX = _backref->_surface->getDrawRect().x + _backref->_surface->getDrawRect().width / 2;
+ _backref->_subtitles->renderFrame(_backref->_currFrameIndex, subCenterX - getDrawRect().x);
+ const Graphics::Surface *bottom = _backref->_subtitles->getBottomSubs();
+ if (bottom) {
+ _vm->_screen->drawSurface2(bottom, _drawRect, _clipRect, true, ++_version, nullptr, _backref->_subtitles->kSubtitleAlpha);
+ }
+ if (_backref->_subtitles->getTopSubs())
+ warning("Top subs are unsupported");
+ }
+}
+
void AnimatedSprite::updateFrameInfo() {
debug(8, "AnimatedSprite::updateFrameInfo()");
const AnimFrameInfo &frameInfo = _animResource.getFrameInfo(_currFrameIndex);
diff --git a/engines/neverhood/sprite.h b/engines/neverhood/sprite.h
index ce8e45c6661..28f0f83da92 100644
--- a/engines/neverhood/sprite.h
+++ b/engines/neverhood/sprite.h
@@ -26,6 +26,7 @@
#include "neverhood/entity.h"
#include "neverhood/graphics.h"
#include "neverhood/resource.h"
+#include "neverhood/subtitles.h"
namespace Neverhood {
@@ -56,6 +57,7 @@ public:
~Sprite() override;
void init() {}
BaseSurface *getSurface() { return _surface; }
+ virtual BaseSurface *getSubtitleSurface() { return nullptr; }
void updateBounds();
void setDoDeltaX(int type);
void setDoDeltaY(int type);
@@ -149,8 +151,20 @@ public:
int16 getFrameIndex(uint32 frameHash) { return _animResource.getFrameIndex(frameHash); }
void setNewHashListIndex(int value) { _newStickFrameIndex = value; }
void startAnimation(uint32 fileHash, int16 plFirstFrameIndex, int16 plLastFrameIndex);
+ BaseSurface *getSubtitleSurface() override { return &_subtitleSurface; }
protected:
+ class AnimatedSpriteSubtitles : public BaseSurface {
+ public:
+ void draw() override;
+ AnimatedSpriteSubtitles(NeverhoodEngine *vm, AnimatedSprite *backRef);
+ private:
+ AnimatedSprite *_backref;
+ };
+
+ static const int kSubtitleWidth = 320;
+ AnimatedSpriteSubtitles _subtitleSurface;
typedef void (AnimatedSprite::*AnimationCb)();
+ Common::ScopedPtr<SubtitlePlayer> _subtitles;
AnimResource _animResource;
uint32 _currAnimFileHash, _newAnimFileHash, _nextAnimFileHash;
int16 _currFrameIndex, _lastFrameIndex;
Commit: 64fb44790e5e194dc4497c9400dcd644060101ba
https://github.com/scummvm/scummvm/commit/64fb44790e5e194dc4497c9400dcd644060101ba
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Enable subtitle on/off if .nhc is available
Changed paths:
engines/neverhood/detection.cpp
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
index 856333bcef5..34b1ea9ebf6 100644
--- a/engines/neverhood/detection.cpp
+++ b/engines/neverhood/detection.cpp
@@ -134,7 +134,36 @@ static const ADGameDescription gameDescriptions[] = {
class NeverhoodMetaEngineDetection : public AdvancedMetaEngineDetection {
public:
NeverhoodMetaEngineDetection() : AdvancedMetaEngineDetection(Neverhood::gameDescriptions, sizeof(ADGameDescription), neverhoodGames) {
- _guiOptions = GUIO5(GUIO_NOSUBTITLES, GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_SKIP_HALL_OF_RECORDS, GAMEOPTION_SCALE_MAKING_OF_VIDEOS);
+ _guiOptions = GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_SKIP_HALL_OF_RECORDS, GAMEOPTION_SCALE_MAKING_OF_VIDEOS);
+ }
+
+ DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) override {
+ DetectedGames detGames(AdvancedMetaEngineDetection::detectGames(fslist, skipADFlags, skipIncomplete));
+ bool hasSubs = false;
+
+ if (detGames.empty())
+ return detGames;
+
+ for (Common::FSList::const_iterator dr = fslist.begin(); dr != fslist.end() && !hasSubs; dr++) {
+ if (dr->getName().equalsIgnoreCase("language") && dr->isDirectory()) {
+ Common::FSList files;
+ if (!dr->getChildren(files, Common::FSNode::kListAll))
+ continue;
+ for (Common::FSList::const_iterator file = files.begin(); file != files.end(); file++) {
+ Common::String fname = file->getName();
+ if (fname.matchString("*.nhc", true) && !fname.equalsIgnoreCase("Fargusfx.nhc")) {
+ hasSubs = true;
+ break;
+ }
+ }
+ }
+ }
+ if (hasSubs)
+ return detGames;
+ for (DetectedGames::iterator it = detGames.begin(); it != detGames.end(); it++) {
+ it->appendGUIOptions("sndNoSubs");
+ }
+ return detGames;
}
const char *getName() const override {
Commit: 7bb97c18d0db44a53443b49b24397b0d2d3705c5
https://github.com/scummvm/scummvm/commit/7bb97c18d0db44a53443b49b24397b0d2d3705c5
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Make subtitles being able to turn off/on
Changed paths:
engines/neverhood/neverhood.cpp
diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp
index e7f53b91552..90a9f5b13b8 100644
--- a/engines/neverhood/neverhood.cpp
+++ b/engines/neverhood/neverhood.cpp
@@ -102,20 +102,22 @@ Common::Error NeverhoodEngine::run() {
Common::String nhcFile = ConfMan.get("nhc_file");
if (!nhcFile.empty() && _res->addNhcArchive("language/" + nhcFile + ".nhc")) {
- Common::SeekableReadStream *s = _res->createNhcStream(0x544E4F46, kResNhcTypeSubFont);
- if (s && s->size() >= 4096) {
- for (uint i = 0; i < 256; i++) {
- s->read(&_subFont[i].bitmap, sizeof(_subFont[i].bitmap));
- for (uint j = 0; j < 16; j++)
- _subFont[i].outline[j] = (_subFont[i].bitmap[j] << 1) | (_subFont[i].bitmap[j] >> 1);
- for (uint j = 1; j < 16; j++)
- _subFont[i].outline[j] |= _subFont[i].bitmap[j-1];
- for (uint j = 0; j < 15; j++)
- _subFont[i].outline[j] |= _subFont[i].bitmap[j+1];
- for (uint j = 0; j < 16; j++)
- _subFont[i].outline[j] &= ~_subFont[i].bitmap[j];
+ if (ConfMan.getBool("subtitles")) {
+ Common::SeekableReadStream *s = _res->createNhcStream(0x544E4F46, kResNhcTypeSubFont);
+ if (s && s->size() >= 4096) {
+ for (uint i = 0; i < 256; i++) {
+ s->read(&_subFont[i].bitmap, sizeof(_subFont[i].bitmap));
+ for (uint j = 0; j < 16; j++)
+ _subFont[i].outline[j] = (_subFont[i].bitmap[j] << 1) | (_subFont[i].bitmap[j] >> 1);
+ for (uint j = 1; j < 16; j++)
+ _subFont[i].outline[j] |= _subFont[i].bitmap[j-1];
+ for (uint j = 0; j < 15; j++)
+ _subFont[i].outline[j] |= _subFont[i].bitmap[j+1];
+ for (uint j = 0; j < 16; j++)
+ _subFont[i].outline[j] &= ~_subFont[i].bitmap[j];
+ }
+ _haveSubtitles = true;
}
- _haveSubtitles = true;
}
}
Commit: 41aa78b8a6d87529846b06e527755fce089b0e50
https://github.com/scummvm/scummvm/commit/41aa78b8a6d87529846b06e527755fce089b0e50
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEVERHOOD: Add ability to support for repeating useful Willie's hint
Changed paths:
engines/neverhood/detection.cpp
engines/neverhood/detection.h
engines/neverhood/dialogs.cpp
engines/neverhood/dialogs.h
engines/neverhood/modules/module1000.cpp
engines/neverhood/modules/module1000.h
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
index 34b1ea9ebf6..a3d9ef6534f 100644
--- a/engines/neverhood/detection.cpp
+++ b/engines/neverhood/detection.cpp
@@ -134,7 +134,8 @@ static const ADGameDescription gameDescriptions[] = {
class NeverhoodMetaEngineDetection : public AdvancedMetaEngineDetection {
public:
NeverhoodMetaEngineDetection() : AdvancedMetaEngineDetection(Neverhood::gameDescriptions, sizeof(ADGameDescription), neverhoodGames) {
- _guiOptions = GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_SKIP_HALL_OF_RECORDS, GAMEOPTION_SCALE_MAKING_OF_VIDEOS);
+ _guiOptions = GUIO5(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_SKIP_HALL_OF_RECORDS,
+ GAMEOPTION_SCALE_MAKING_OF_VIDEOS, GAMEOPTION_REPEAT_WILLIE_HINT);
}
DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) override {
diff --git a/engines/neverhood/detection.h b/engines/neverhood/detection.h
index e15505a5aff..1a403957583 100644
--- a/engines/neverhood/detection.h
+++ b/engines/neverhood/detection.h
@@ -31,6 +31,7 @@ enum NeverhoodGameFeatures {
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#define GAMEOPTION_SKIP_HALL_OF_RECORDS GUIO_GAMEOPTIONS2
#define GAMEOPTION_SCALE_MAKING_OF_VIDEOS GUIO_GAMEOPTIONS3
+#define GAMEOPTION_REPEAT_WILLIE_HINT GUIO_GAMEOPTIONS4
} // End of namespace Neverhood
diff --git a/engines/neverhood/dialogs.cpp b/engines/neverhood/dialogs.cpp
index b458fbe2250..9270587b223 100644
--- a/engines/neverhood/dialogs.cpp
+++ b/engines/neverhood/dialogs.cpp
@@ -58,6 +58,11 @@ NeverhoodOptionsWidget::NeverhoodOptionsWidget(GuiObject *boss, const Common::St
"NeverhoodGameOptionsDialog.ScaleMakingOfVideos", _("Scale the making of videos to full screen"),
_("Scale the making of videos, so that they use the whole screen"));
+ _repeatWillieHint = new GUI::CheckboxWidget(
+ widgetsBoss(),
+ "NeverhoodGameOptionsDialog.RepeatWillieHint", _("Repeat useful Willie's hint"),
+ _("Repeat actual useful hint by Willie"));
+
Common::String path = ConfMan.get("path", _domain);
Common::FSDirectory dir(path);
Common::FSDirectory *langdir = dir.getSubDirectory("language");
@@ -94,6 +99,7 @@ void NeverhoodOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common:
.addWidget("OriginalSaveLoad", "Checkbox")
.addWidget("SkipHallOfRecords", "Checkbox")
.addWidget("ScaleMakingOfVideos", "Checkbox")
+ .addWidget("RepeatWillieHint", "Checkbox")
.addLayout(GUI::ThemeLayout::kLayoutHorizontal)
.addPadding(0, 0, 0, 0)
.addWidget("NhcDesc", "OptionsLabel")
@@ -116,6 +122,10 @@ void NeverhoodOptionsWidget::load() {
_scaleMakingOfVideosCheckbox->setState(ConfMan.getBool("scalemakingofvideos", _domain));
}
+ if (_repeatWillieHint) {
+ _repeatWillieHint->setState(ConfMan.getBool("repeatwilliehint", _domain));
+ }
+
if (_nhcPopUp) {
Common::String nhcFile(ConfMan.get("nhc_file", _domain));
for (uint i = 0; i < _nhcFiles.size(); i++)
@@ -137,6 +147,10 @@ bool NeverhoodOptionsWidget::save() {
ConfMan.setBool("scalemakingofvideos", _scaleMakingOfVideosCheckbox->getState(), _domain);
}
+ if (_repeatWillieHint) {
+ ConfMan.setBool("repeatwilliehint", _repeatWillieHint->getState(), _domain);
+ }
+
if (_nhcPopUp) {
uint32 selectedNhcFile = _nhcPopUp->getSelectedTag();
if (selectedNhcFile < _nhcFiles.size())
diff --git a/engines/neverhood/dialogs.h b/engines/neverhood/dialogs.h
index 0c683e6d5ba..0999b132642 100644
--- a/engines/neverhood/dialogs.h
+++ b/engines/neverhood/dialogs.h
@@ -57,6 +57,7 @@ private:
GUI::CheckboxWidget *_originalSaveLoadCheckbox;
GUI::CheckboxWidget *_skipHallOfRecordsCheckbox;
GUI::CheckboxWidget *_scaleMakingOfVideosCheckbox;
+ GUI::CheckboxWidget *_repeatWillieHint;
GUI::PopUpWidget *_nhcPopUp;
Common::StringArray _nhcFiles;
diff --git a/engines/neverhood/modules/module1000.cpp b/engines/neverhood/modules/module1000.cpp
index 03d2793207b..23892efd79f 100644
--- a/engines/neverhood/modules/module1000.cpp
+++ b/engines/neverhood/modules/module1000.cpp
@@ -22,6 +22,8 @@
#include "neverhood/modules/module1000.h"
#include "neverhood/modules/module1000_sprites.h"
+#include "common/config-manager.h"
+
namespace Neverhood {
Module1000::Module1000(NeverhoodEngine *vm, Module *parentModule, int which)
@@ -686,7 +688,7 @@ uint32 Scene1005::getTextIndex() {
textIndex = getKloggsTextIndex();
}
if (getGlobalVar(V_TEXT_FLAG1) && getGlobalVar(V_TEXT_INDEX) == textIndex) {
- textIndex = getTextIndex3();
+ textIndex = getTextIndex3(textIndex);
} else {
setGlobalVar(V_TEXT_FLAG1, 1);
setGlobalVar(V_TEXT_INDEX, textIndex);
@@ -773,8 +775,12 @@ uint32 Scene1005::getKloggsTextIndex() {
return textIndex + 40;
}
-uint32 Scene1005::getTextIndex3() {
+uint32 Scene1005::getTextIndex3(uint32 usefulHint) {
uint32 textIndex = getGlobalVar(V_TEXT_COUNTING_INDEX2);
+ if (textIndex + 1 > 10 && ConfMan.getBool("repeatwilliehint")) {
+ setGlobalVar(V_TEXT_COUNTING_INDEX2, 0);
+ return usefulHint;
+ }
if (textIndex + 1 > 10) {
textIndex = 0;
}
diff --git a/engines/neverhood/modules/module1000.h b/engines/neverhood/modules/module1000.h
index 35c68f8d601..229b40dc198 100644
--- a/engines/neverhood/modules/module1000.h
+++ b/engines/neverhood/modules/module1000.h
@@ -101,7 +101,7 @@ protected:
uint32 getTextIndex();
uint32 getTextIndex1();
uint32 getKloggsTextIndex();
- uint32 getTextIndex3();
+ uint32 getTextIndex3(uint32 usefulHint);
};
} // End of namespace Neverhood
Commit: eec64ea340d0c051a70fb7e20bdf33f247cf28ea
https://github.com/scummvm/scummvm/commit/eec64ea340d0c051a70fb7e20bdf33f247cf28ea
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-31T20:47:10+01:00
Commit Message:
NEWS: Add entry about supporting Ctpax-Che at ter & Rigel localizations.
Changed paths:
NEWS.md
diff --git a/NEWS.md b/NEWS.md
index 0ae46a6f4db..1854ca2f28a 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -60,6 +60,7 @@ For a more comprehensive changelog of the latest experimental code, see:
Neverhood:
- Added support for Japanese version of Neverhood.
+ - Support localizations by Ctpax-Che at ter & Rigel.
Plumbers:
- Fixed crash with windows version.
More information about the Scummvm-git-logs
mailing list