[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