[Scummvm-git-logs] scummvm master -> 98dea5189af4ecb94df7119a1ce618915b47805e
whoozle
noreply at scummvm.org
Sun Oct 11 15:18:18 UTC 2026
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
535ed1d9c7 COMMON: FORMATS: NE executable uses named const for invalid address
98dea5189a COMMON: FORMATS: Read NewExe segmentation and provide accessors
Commit: 535ed1d9c759fa341eb86fda10c3d4f9825f434b
https://github.com/scummvm/scummvm/commit/535ed1d9c759fa341eb86fda10c3d4f9825f434b
Author: Vladimir Menshakov (vladimir.menshakov at gmail.com)
Date: 2026-10-11T16:14:12+01:00
Commit Message:
COMMON: FORMATS: NE executable uses named const for invalid address
Formatted sources.
Changed paths:
common/formats/winexe_ne.cpp
common/formats/winexe_ne.h
diff --git a/common/formats/winexe_ne.cpp b/common/formats/winexe_ne.cpp
index a2aefcb92a9..fe217a0ed65 100644
--- a/common/formats/winexe_ne.cpp
+++ b/common/formats/winexe_ne.cpp
@@ -19,10 +19,10 @@
*
*/
+#include "common/formats/winexe_ne.h"
#include "common/debug.h"
#include "common/str.h"
#include "common/stream.h"
-#include "common/formats/winexe_ne.h"
namespace Common {
@@ -54,7 +54,7 @@ bool NEResources::loadFromEXE(SeekableReadStream *stream, DisposeAfterUse::Flag
_disposeFileHandle = disposeFileHandle;
uint32 offsetResourceTable = getResourceTableOffset();
- if (offsetResourceTable == 0xFFFFFFFF)
+ if (offsetResourceTable == kInvalidAddress)
return false;
if (offsetResourceTable == 0)
return true;
@@ -67,28 +67,28 @@ bool NEResources::loadFromEXE(SeekableReadStream *stream, DisposeAfterUse::Flag
uint32 NEResources::getResourceTableOffset() {
if (!_exe)
- return 0xFFFFFFFF;
+ return kInvalidAddress;
if (!_exe->seek(0))
- return 0xFFFFFFFF;
+ return kInvalidAddress;
// 'MZ'
if (_exe->readUint16BE() != 0x4D5A)
- return 0xFFFFFFFF;
+ return kInvalidAddress;
if (!_exe->seek(60))
- return 0xFFFFFFFF;
+ return kInvalidAddress;
uint32 offsetSegmentEXE = _exe->readUint16LE();
if (!_exe->seek(offsetSegmentEXE))
- return 0xFFFFFFFF;
+ return kInvalidAddress;
// 'NE'
if (_exe->readUint16BE() != 0x4E45)
- return 0xFFFFFFFF;
+ return kInvalidAddress;
if (!_exe->seek(offsetSegmentEXE + 36))
- return 0xFFFFFFFF;
+ return kInvalidAddress;
uint32 offsetResourceTable = _exe->readUint16LE();
if (offsetResourceTable == 0)
@@ -98,7 +98,7 @@ uint32 NEResources::getResourceTableOffset() {
// Offset relative to the segment _exe header
offsetResourceTable += offsetSegmentEXE;
if (!_exe->seek(offsetResourceTable))
- return 0xFFFFFFFF;
+ return kInvalidAddress;
return offsetResourceTable;
}
@@ -119,7 +119,7 @@ uint32 NEResources::getResourceTableOffset() {
* CHAR szName[]; // null-terminated name
* }
*
-*/
+ */
bool NEResources::readNameTable(uint32 offset, uint32 size) {
if (!_exe)
@@ -138,7 +138,7 @@ bool NEResources::readNameTable(uint32 offset, uint32 size) {
break;
uint16 resourceType = _exe->readUint16LE();
- uint16 resourceId = _exe->readUint16LE();
+ uint16 resourceId = _exe->readUint16LE();
resourceId &= 0x7FFF;
_exe->skip(1); // paddingZero
@@ -162,8 +162,7 @@ static const char *const s_resTypeNames[] = {
"font_dir", "font", "accelerator", "rc_data", "msg_table",
"group_cursor", "", "group_icon", "name_table", "version", "dlg_include",
"", "plug_play", "vxd", "ani_cursor", "ani_icon", "html",
- "manifest"
-};
+ "manifest"};
bool NEResources::readResourceTable(uint32 offset) {
if (!_exe)
@@ -199,11 +198,11 @@ bool NEResources::readResourceTable(uint32 offset) {
// Resource properties
res.offset = _exe->readUint16LE() * align;
- res.size = _exe->readUint16LE() * align;
- res.flags = _exe->readUint16LE();
- uint16 id = _exe->readUint16LE();
+ res.size = _exe->readUint16LE() * align;
+ res.flags = _exe->readUint16LE();
+ uint16 id = _exe->readUint16LE();
res.handle = _exe->readUint16LE();
- res.usage = _exe->readUint16LE();
+ res.usage = _exe->readUint16LE();
res.type = type;
diff --git a/common/formats/winexe_ne.h b/common/formats/winexe_ne.h
index 2aab380c211..f039d0fae3e 100644
--- a/common/formats/winexe_ne.h
+++ b/common/formats/winexe_ne.h
@@ -22,9 +22,9 @@
#ifndef COMMON_WINEXE_NE_H
#define COMMON_WINEXE_NE_H
+#include "common/formats/winexe.h"
#include "common/list.h"
#include "common/str.h"
-#include "common/formats/winexe.h"
namespace Common {
@@ -37,7 +37,8 @@ namespace Common {
* @{
*/
-template<class T> class Array;
+template<class T>
+class Array;
class SeekableReadStream;
/**
@@ -51,6 +52,8 @@ public:
NEResources();
~NEResources();
+ static constexpr uint32 kInvalidAddress = 0xFFFFFFFF;
+
/** Clear all information. */
void clear() override;
@@ -87,7 +90,7 @@ private:
uint16 usage;
};
- SeekableReadStream *_exe; ///< Current file.
+ SeekableReadStream *_exe; ///< Current file.
DisposeAfterUse::Flag _disposeFileHandle;
/** All resources. */
Commit: 98dea5189af4ecb94df7119a1ce618915b47805e
https://github.com/scummvm/scummvm/commit/98dea5189af4ecb94df7119a1ce618915b47805e
Author: Vladimir Menshakov (vladimir.menshakov at gmail.com)
Date: 2026-10-11T16:14:21+01:00
Commit Message:
COMMON: FORMATS: Read NewExe segmentation and provide accessors
Remove PhoenixVR NewExe parser.
Changed paths:
common/formats/winexe_ne.cpp
common/formats/winexe_ne.h
engines/phoenixvr/phoenixvr.cpp
engines/phoenixvr/wise.cpp
diff --git a/common/formats/winexe_ne.cpp b/common/formats/winexe_ne.cpp
index fe217a0ed65..ae6f30d81dd 100644
--- a/common/formats/winexe_ne.cpp
+++ b/common/formats/winexe_ne.cpp
@@ -87,6 +87,40 @@ uint32 NEResources::getResourceTableOffset() {
if (_exe->readUint16BE() != 0x4E45)
return kInvalidAddress;
+ // Reading automatic segment index
+ if (!_exe->seek(offsetSegmentEXE + 0x0e))
+ return kInvalidAddress;
+ _autoSegmentIndex = _exe->readUint16LE();
+
+ // number of NE segments
+ if (!_exe->seek(offsetSegmentEXE + 0x001c))
+ return kInvalidAddress;
+ auto numSegments = _exe->readUint16LE();
+
+ // segment table offset
+ if (!_exe->seek(offsetSegmentEXE + 0x0022))
+ return kInvalidAddress;
+ auto segmentTableOffset = _exe->readUint16LE();
+
+ // segment address alignment (page size)
+ if (!_exe->seek(offsetSegmentEXE + 0x0032))
+ return kInvalidAddress;
+ auto segmentAddrShift = _exe->readUint16LE();
+
+ if (!_exe->seek(offsetSegmentEXE + segmentTableOffset))
+ return kInvalidAddress;
+ _segments.reserve(numSegments);
+ for (unsigned idx = 0; idx != numSegments; ++idx) {
+ uint32 segmentOffset = _exe->readUint16LE();
+ segmentOffset <<= segmentAddrShift;
+ uint32 segmentSize = _exe->readUint16LE();
+ if (segmentSize == 0)
+ segmentSize = 0x10000;
+ auto segmentFlags = _exe->readUint16LE();
+ _exe->skip(2);
+ _segments.push_back(NESegment{segmentOffset, segmentSize, segmentFlags});
+ }
+
if (!_exe->seek(offsetSegmentEXE + 36))
return kInvalidAddress;
diff --git a/common/formats/winexe_ne.h b/common/formats/winexe_ne.h
index f039d0fae3e..e96836d4ed3 100644
--- a/common/formats/winexe_ne.h
+++ b/common/formats/winexe_ne.h
@@ -41,6 +41,18 @@ template<class T>
class Array;
class SeekableReadStream;
+// NE executable segment
+struct NESegment {
+ static constexpr uint16 kFlagSingleData = 1;
+ static constexpr uint16 kFlagMultipleData = 2;
+ static constexpr uint16 kFlagLinkError = 0x2000u;
+ static constexpr uint16 kFlagLibModule = 0x8000u;
+
+ uint32 offset;
+ uint32 size;
+ uint16 flags;
+};
+
/**
* A class able to load resources from a Windows New Executable, such
* as cursors, bitmaps, and sounds.
@@ -72,6 +84,12 @@ public:
/** Get a string from a string resource. */
String loadString(uint32 stringID) override;
+ /** Get all NE segments */
+ const Array<NESegment> &getSegments() const { return _segments; }
+
+ /* Index of NE automatic segment */
+ uint32 getAutomaticSegmentIndex() const { return _autoSegmentIndex; }
+
protected:
VersionInfo *parseVersionInfo(SeekableReadStream *stream) override;
@@ -93,6 +111,9 @@ private:
SeekableReadStream *_exe; ///< Current file.
DisposeAfterUse::Flag _disposeFileHandle;
+ uint32 _autoSegmentIndex = 0;
+ Array<NESegment> _segments;
+
/** All resources. */
List<Resource> _resources;
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index b64a3a4f9e2..7ba08b8bc66 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -347,7 +347,11 @@ PhoenixVREngine::PhoenixVREngine(OSystem *syst, const ADGameDescription *gameDes
#if 0
Common::ScopedPtr<Common::File> file(new Common::File);
if (file->open("install.exe")) {
- SearchMan.add("install.exe", createWISEArchive(file.release()), 0, true);
+ auto *wiseArch = createWISEArchive(file.release());
+ if (wiseArch)
+ SearchMan.add("install.exe", wiseArch, 0, true);
+ else
+ debug("found install.exe, but it's not WISE archive");
}
#endif
}
diff --git a/engines/phoenixvr/wise.cpp b/engines/phoenixvr/wise.cpp
index 355d9f92e2b..b83b5a2356c 100644
--- a/engines/phoenixvr/wise.cpp
+++ b/engines/phoenixvr/wise.cpp
@@ -25,6 +25,7 @@
#include "common/compression/deflate.h"
#include "common/crc.h"
#include "common/debug.h"
+#include "common/formats/winexe_ne.h"
#include "common/hashmap.h"
#include "common/list.h"
#include "common/memstream.h"
@@ -33,55 +34,6 @@
namespace PhoenixVR {
namespace {
-struct NE {
- struct Segment {
- uint32 offset;
- uint32 size;
- uint16 flags;
- bool automatic() const {
- return flags & 1;
- }
- };
-
- Common::Array<Segment> segments;
- uint16 autoSegment;
-
- NE(Common::SeekableReadStream &stream) {
- auto magic = stream.readUint16LE();
- if (magic != 0x5a4d)
- error("Not an MZ executable");
- stream.seek(0x3c);
- auto neOffset = stream.readUint32LE();
- stream.seek(neOffset);
- magic = stream.readUint16LE();
- if (magic != 0x454e)
- error("Not an NE executable");
- stream.seek(neOffset + 0x000e);
- autoSegment = stream.readUint16LE();
- debug("auto segment: %u", autoSegment);
- stream.seek(neOffset + 0x001c);
- auto numSegments = stream.readUint16LE();
- stream.seek(neOffset + 0x0022);
- auto segmentTableOffset = stream.readUint16LE();
- stream.seek(neOffset + 0x0032);
- auto segmentAddrShift = stream.readUint16LE();
- stream.seek(neOffset + segmentTableOffset);
- segments.reserve(numSegments);
- for (unsigned idx = 0; idx != numSegments; ++idx) {
- uint32 segmentOffset = stream.readUint16LE();
- segmentOffset <<= segmentAddrShift;
- uint32 segmentSize = stream.readUint16LE();
- if (segmentSize == 0)
- segmentSize = 0x10000;
- auto segmentFlags = stream.readUint16LE();
- auto segmentAlloc = stream.readUint16LE();
- if (idx + 1 == autoSegment && (segmentFlags & 1) == 0)
- error("automatic segment must have flag 1");
- debug("NE: segment %u: offset: %04x, size: %u, flags: %04x, alloc: %u", idx, segmentOffset, segmentSize, segmentFlags, segmentAlloc);
- segments.push_back(Segment{segmentOffset, segmentSize, segmentFlags});
- }
- }
-};
class WiseArchive : public Common::Archive {
Common::ScopedPtr<Common::SeekableReadStream> stream;
@@ -198,17 +150,35 @@ class WiseArchive : public Common::Archive {
}
public:
- WiseArchive(Common::SeekableReadStream *s) : stream(s) {
- NE ne(*s);
+ WiseArchive(Common::SeekableReadStream *s) : stream(s) {}
+
+ static WiseArchive *load(Common::SeekableReadStream *s) {
+ Common::ScopedPtr<Common::SeekableReadStream> stream(s);
+ Common::NEResources ne;
+ if (!ne.loadFromEXE(s, DisposeAfterUse::NO)) {
+ debug("WISE: failed to load NE EXE");
+ return nullptr;
+ }
+
+ auto &segments = ne.getSegments();
+ auto autoSegmentIndex = ne.getAutomaticSegmentIndex();
+ if (autoSegmentIndex == 0) {
+ debug("WISE: NE: no auto segment index");
+ return nullptr;
+ }
+ auto &autoSegment = segments[autoSegmentIndex - 1];
+
long overlayOffset = -1;
- for (auto &seg : ne.segments) {
- if (seg.automatic())
+ for (auto &seg : segments) {
+ if (seg.flags & Common::NESegment::kFlagSingleData)
continue;
stream->seek(seg.offset);
Common::Array<byte> data(seg.size);
- if (stream->read(data.data(), data.size()) != data.size())
- error("NE segment: short read");
+ if (stream->read(data.data(), data.size()) != data.size()) {
+ debug("WISE: NE segment: short read");
+ return nullptr;
+ }
if (overlayOffset < 0) {
overlayOffset = patternFind(data, 0xa1, 30, [&](const byte *src) -> bool {
@@ -232,9 +202,10 @@ public:
if (overlayOffset >= 0) {
auto low = READ_LE_UINT16(data.data() + overlayOffset + 1);
auto high = READ_LE_UINT16(data.data() + overlayOffset + 5);
- if (low + 2 != high)
- error("non-contiguous overlay pointer");
- auto &autoSegment = ne.segments[ne.autoSegment - 1];
+ if (low + 2 != high) {
+ debug("WISE: non-contiguous overlay pointer");
+ return nullptr;
+ }
stream->seek(autoSegment.offset + low);
overlayOffset = stream->readUint32LE();
debug("found overlay at %08lx", overlayOffset);
@@ -242,8 +213,10 @@ public:
}
}
}
- if (overlayOffset < 0)
- error("can't find overlay offset");
+ if (overlayOffset < 0) {
+ debug("WISE: overlay offset wasn't found");
+ return nullptr;
+ }
stream->seek(overlayOffset);
Common::Array<uint> header(16);
@@ -253,29 +226,47 @@ public:
auto &flags = header[0];
auto &advertisedEOF = header[15];
debug("flags: %08x, eof: %08x", flags, advertisedEOF);
- if (advertisedEOF != stream->size())
- error("invalid overlay header - EOF does not match stream size");
+ if (advertisedEOF != stream->size()) {
+ debug("WISE: invalid overlay header - EOF does not match stream size");
+ return nullptr;
+ }
debug("script %u/%u", header[7], header[6]);
Script script;
script.script = inflate(*stream, header[7]);
- if (script.script.size() != header[6])
- error("script uncompressed size does not match header");
+ if (script.script.size() != header[6]) {
+ debug("WISE: script uncompressed size does not match header");
+ return nullptr;
+ }
debug("packed dll at %08x, size: %u", (uint32)stream->pos(), header[8]);
Common::Array<byte> dllData;
dllData = inflate(*stream, header[8]);
Common::MemoryReadStream dllStream(dllData.data(), dllData.size());
- NE dll(dllStream);
- for (auto &seg : dll.segments) {
- if (seg.automatic())
+ Common::NEResources dll;
+ if (!dll.loadFromEXE(&dllStream, DisposeAfterUse::NO)) {
+ debug("WISE: can't load NE overlay DLL");
+ return nullptr;
+ }
+ auto dllAutoSegmentIndex = dll.getAutomaticSegmentIndex();
+ if (dllAutoSegmentIndex == 0) {
+ debug("WISE: DLL: no auto segment index");
+ return nullptr;
+ }
+ auto &dllSegments = dll.getSegments();
+ auto &dllAutoSegment = dllSegments[dllAutoSegmentIndex - 1];
+
+ for (auto &seg : dllSegments) {
+ if (seg.flags & Common::NESegment::kFlagSingleData)
continue;
dllStream.seek(seg.offset);
Common::Array<byte> data(seg.size);
- if (dllStream.read(data.data(), data.size()) != data.size())
- error("NE segment: short read");
+ if (dllStream.read(data.data(), data.size()) != data.size()) {
+ debug("WISE: NE segment: short read");
+ return nullptr;
+ }
auto instructionTables = patternFind(data, 0x8a, 26, [&](const byte *src) -> bool {
return src[0] == 0x8a &&
@@ -306,12 +297,11 @@ public:
assert(stringsOffset + script.numStrings.size() <= data.size());
assert(localizedStringsOffset + script.numLocalized.size() <= data.size());
assert(numBytesOffset + script.numBytes.size() <= data.size());
- auto &autoSegment = dll.segments[dll.autoSegment - 1];
- dllStream.seek(autoSegment.offset + stringsOffset);
+ dllStream.seek(dllAutoSegment.offset + stringsOffset);
dllStream.read(script.numStrings.data(), script.numStrings.size());
- dllStream.seek(autoSegment.offset + localizedStringsOffset);
+ dllStream.seek(dllAutoSegment.offset + localizedStringsOffset);
dllStream.read(script.numLocalized.data(), script.numLocalized.size());
- dllStream.seek(autoSegment.offset + numBytesOffset);
+ dllStream.seek(dllAutoSegment.offset + numBytesOffset);
dllStream.read(script.numBytes.data(), script.numBytes.size());
break;
}
@@ -338,11 +328,14 @@ public:
if (header[13])
skipStream(header[13]);
- baseStreamsOffset = stream->pos();
- debug("base stream offset: %08x", (uint32)baseStreamsOffset);
+ uint32 offset = stream->pos();
+ debug("base stream offset: %08x", offset);
+ Common::ScopedPtr<WiseArchive> wise(new WiseArchive(stream.release()));
+ wise->baseStreamsOffset = offset;
for (auto entry : script.parse()) {
- _entries.setVal(Common::move(entry.path), {entry.begin, entry.end});
+ wise->_entries.setVal(Common::move(entry.path), {entry.begin, entry.end});
}
+ return wise.release();
}
char getPathSeparator() const override {
@@ -384,6 +377,6 @@ public:
} // namespace
Common::Archive *createWISEArchive(Common::SeekableReadStream *stream) {
- return new WiseArchive(stream);
+ return WiseArchive::load(stream);
}
} // namespace PhoenixVR
More information about the Scummvm-git-logs
mailing list