[Scummvm-git-logs] scummvm master -> f216fd288060e14f8811629bd670588c5b128a0b

sev- noreply at scummvm.org
Sun Jun 21 22:47:33 UTC 2026


This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
f651362f97 DIRECTOR: Stub DateTime Xtra for the Löwenzahn, TKKG and Oscar series of games
8d254fbbe5 DIRECTOR: Stub NetLingo Xtra for the Löwenzahn, TKKG & Oscar series of games
9034771ccf DIRECTOR: Stub PaintX Xtra for Löwenzahn 2
c79750df71 DIRECTOR: Stub SetMouse (SMXTRA) Xtra for Löwenzahn 2-4
846ab92664 DIRECTOR: LINGO: Implement FileXtra4
d535c2f9a7 DIRECTOR: LINGO: Implement FileXtra DirectoryExists and DirectoryToList
4bb528d1a6 DIRECTOR: Load Xtras from nested folders
a2f863f792 DIRECTOR: LINGO: Recognise QTASSET as the QuickTime asset Xtra
e8c1920cfb DIRECTOR: LINGO: Fix FileXtra DirectoryToList path resolution
f216fd2880 DIRECTOR: List German Terzio/Tivola titles in Xtra USED IN blocks


Commit: f651362f9763ea3730eb7f9817ae21be4370ab5b
    https://github.com/scummvm/scummvm/commit/f651362f9763ea3730eb7f9817ae21be4370ab5b
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub DateTime Xtra for the Löwenzahn, TKKG and Oscar series of games

Changed paths:
  A engines/director/lingo/xtras/d/datetime.cpp
  A engines/director/lingo/xtras/d/datetime.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 4355af923f4..a8231502aab 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -160,12 +160,12 @@
 #include "director/lingo/xtras/b/budapi.h"
 #include "director/lingo/xtras/d/directsound.h"
 #include "director/lingo/xtras/d/displayres.h"
+#include "director/lingo/xtras/d/datetime.h"
 #include "director/lingo/xtras/f/filextra.h"
 #include "director/lingo/xtras/g/getdir.h"
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
 #include "director/lingo/xtras/m/mui.h"
-#include "director/lingo/xtras/m/mui.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
@@ -276,6 +276,7 @@ static const struct XLibProto {
 	XLIBDEF(DPWAVIXObj,			kXObj,			300),	// D3
 	XLIBDEF(DPWQTWXObj,			kXObj,			300),	// D3
 	XLIBDEF(DarkenScreen,		kXObj,			300),	// D3
+	XLIBDEF(DateTimeXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(DateUtilXObj,		kXObj,			400),	// D4
 	XLIBDEF(DeveloperStack,		kXObj,			300),	// D3
 	XLIBDEF(DialogsXObj,		kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/d/datetime.cpp b/engines/director/lingo/xtras/d/datetime.cpp
new file mode 100644
index 00000000000..dc682d9cc0f
--- /dev/null
+++ b/engines/director/lingo/xtras/d/datetime.cpp
@@ -0,0 +1,174 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/d/datetime.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ *
+ **************************************************/
+
+/*
+-- xtra DateTimeXtra    -- Version 1.0.1 (32 Bit)
+
+-- --------------------------------------------------------------------
+-- this Xtra returns date and time related information
+-- --------------------------------------------------------------------
+
+-- �1997 by Stephan Eichhorn, Scirius Multimedia
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+-- global methods --
+--------------------------------------------------------
+* GetSeconds         -- get the seconds (0...59) 
+* GetMinutes         -- get the minutes (0...59) 
+* GetHours           -- get the hours (0...23) 
+* GetDay             -- get the day (1...31) 
+* GetMonth           -- get the month (1...12) 
+* GetYear            -- get the year  
+* GetWeekDay         -- get the weekday (0...6) 0=sunday
+--------------------------------------------------------
+
+
+ */
+
+namespace Director {
+
+const char *DateTimeXtra::xlibName = "DateTime";
+const XlibFileDesc DateTimeXtra::fileNames[] = {
+	{ "DATETIME",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "GetSeconds", DateTimeXtra::m_GetSeconds, 0, 0, 500, HBLTIN },
+	{ "GetMinutes", DateTimeXtra::m_GetMinutes, 0, 0, 500, HBLTIN },
+	{ "GetHours", DateTimeXtra::m_GetHours, 0, 0, 500, HBLTIN },
+	{ "GetDay", DateTimeXtra::m_GetDay, 0, 0, 500, HBLTIN },
+	{ "GetMonth", DateTimeXtra::m_GetMonth, 0, 0, 500, HBLTIN },
+	{ "GetYear", DateTimeXtra::m_GetYear, 0, 0, 500, HBLTIN },
+	{ "GetWeekDay", DateTimeXtra::m_GetWeekDay, 0, 0, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+DateTimeXtraObject::DateTimeXtraObject(ObjectType ObjectType) :Object<DateTimeXtraObject>("DateTime") {
+	_objType = ObjectType;
+}
+
+bool DateTimeXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum DateTimeXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(DateTimeXtra::xlibName);
+	warning("DateTimeXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void DateTimeXtra::open(ObjectType type, const Common::Path &path) {
+    DateTimeXtraObject::initMethods(xlibMethods);
+    DateTimeXtraObject *xobj = new DateTimeXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void DateTimeXtra::close(ObjectType type) {
+    DateTimeXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void DateTimeXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("DateTimeXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void DateTimeXtra::m_GetSeconds(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_sec));
+}
+
+void DateTimeXtra::m_GetMinutes(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_min));
+}
+
+void DateTimeXtra::m_GetHours(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_hour));
+}
+
+void DateTimeXtra::m_GetDay(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_mday));
+}
+
+void DateTimeXtra::m_GetMonth(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_mon + 1));
+}
+
+void DateTimeXtra::m_GetYear(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_year + 1900));
+}
+
+void DateTimeXtra::m_GetWeekDay(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_wday));
+}
+
+}
diff --git a/engines/director/lingo/xtras/d/datetime.h b/engines/director/lingo/xtras/d/datetime.h
new file mode 100644
index 00000000000..3d06f046795
--- /dev/null
+++ b/engines/director/lingo/xtras/d/datetime.h
@@ -0,0 +1,56 @@
+/* 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 DIRECTOR_LINGO_XTRAS_D_DATETIME_H
+#define DIRECTOR_LINGO_XTRAS_D_DATETIME_H
+
+namespace Director {
+
+class DateTimeXtraObject : public Object<DateTimeXtraObject> {
+public:
+	DateTimeXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace DateTimeXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_GetSeconds(int nargs);
+void m_GetMinutes(int nargs);
+void m_GetHours(int nargs);
+void m_GetDay(int nargs);
+void m_GetMonth(int nargs);
+void m_GetYear(int nargs);
+void m_GetWeekDay(int nargs);
+
+} // End of namespace DateTimeXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index bc153d60146..c7d636fa74e 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -197,6 +197,7 @@ MODULE_OBJS = \
 	lingo/xtras/k/keypoll.o \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
+	lingo/xtras/d/datetime.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \


Commit: 8d254fbbe51d82302be48b81fd95c8f1596b0bc3
    https://github.com/scummvm/scummvm/commit/8d254fbbe51d82302be48b81fd95c8f1596b0bc3
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub NetLingo Xtra for the Löwenzahn, TKKG & Oscar series of games

Changed paths:
  A engines/director/lingo/xtras/lingo - Verknüpfung.lnk
  A engines/director/lingo/xtras/n/netlingo.cpp
  A engines/director/lingo/xtras/n/netlingo.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index a8231502aab..ae9ff02f3b1 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -166,6 +166,7 @@
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
 #include "director/lingo/xtras/m/mui.h"
+#include "director/lingo/xtras/n/netlingo.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
@@ -342,6 +343,7 @@ static const struct XLibProto {
 	XLIBDEF(MuiXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(MyFolderXObj,			kXObj,					400),	// D4
 	XLIBDEF(MystIsleXObj,		kXObj,			400),	// D4
+	XLIBDEF(NetLingoXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(OSCheckXtra,		kXtraObj,		400),	// D4
 	XLIBDEF(OpenBleedWindowXCMD,kXObj,			300),	// D3
 	XLIBDEF(OpenURLXtra,		kXtraObj,		500),	// D5
diff --git "a/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk" "b/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk"
new file mode 100644
index 00000000000..ca42fcdf9f5
Binary files /dev/null and "b/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk" differ
diff --git a/engines/director/lingo/xtras/n/netlingo.cpp b/engines/director/lingo/xtras/n/netlingo.cpp
new file mode 100644
index 00000000000..f3f58b8a0c0
--- /dev/null
+++ b/engines/director/lingo/xtras/n/netlingo.cpp
@@ -0,0 +1,164 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/n/netlingo.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 4 / 7 / 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist Discovers the Sea
+ *
+ **************************************************/
+
+/*
+-- xtra NetLingo 
+* netStatus * --  
+* getNetText  * -- 
+* gotoNetMovie * -- 
+* preloadNetThing  * -- 
+* netAbort * -- 
+* gotoNetPage * -- 
+* getLatestNetID * -- 
+* netError * -- 
+* netDone * -- 
+* netTextResult * -- 
+* netMime * -- 
+* netLastModDate * -- 
+* externalEvent * -- 
+* netPresent * -- 
+* downloadNetThing  * -- 
+* clearCache * -- 
+* cacheSize * -- 
+* cacheDocVerify * -- 
+* proxyServer * -- 
+* browserName * -- 
+* tellStreamStatus * -- 
+
+ */
+
+namespace Director {
+
+const char *NetLingoXtra::xlibName = "NetLingo";
+const XlibFileDesc NetLingoXtra::fileNames[] = {
+	{ "netlingo",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "netStatus", NetLingoXtra::m_netStatus, -1, 0, 500, HBLTIN },
+	{ "getNetText", NetLingoXtra::m_getNetText, -1, 0, 500, HBLTIN },
+	{ "gotoNetMovie", NetLingoXtra::m_gotoNetMovie, -1, 0, 500, HBLTIN },
+	{ "preloadNetThing", NetLingoXtra::m_preloadNetThing, -1, 0, 500, HBLTIN },
+	{ "netAbort", NetLingoXtra::m_netAbort, -1, 0, 500, HBLTIN },
+	{ "gotoNetPage", NetLingoXtra::m_gotoNetPage, -1, 0, 500, HBLTIN },
+	{ "getLatestNetID", NetLingoXtra::m_getLatestNetID, -1, 0, 500, HBLTIN },
+	{ "netError", NetLingoXtra::m_netError, -1, 0, 500, HBLTIN },
+	{ "netDone", NetLingoXtra::m_netDone, -1, 0, 500, HBLTIN },
+	{ "netTextResult", NetLingoXtra::m_netTextResult, -1, 0, 500, HBLTIN },
+	{ "netMime", NetLingoXtra::m_netMime, -1, 0, 500, HBLTIN },
+	{ "netLastModDate", NetLingoXtra::m_netLastModDate, -1, 0, 500, HBLTIN },
+	{ "externalEvent", NetLingoXtra::m_externalEvent, -1, 0, 500, HBLTIN },
+	{ "netPresent", NetLingoXtra::m_netPresent, -1, 0, 500, HBLTIN },
+	{ "downloadNetThing", NetLingoXtra::m_downloadNetThing, -1, 0, 500, HBLTIN },
+	{ "clearCache", NetLingoXtra::m_clearCache, -1, 0, 500, HBLTIN },
+	{ "cacheSize", NetLingoXtra::m_cacheSize, -1, 0, 500, HBLTIN },
+	{ "cacheDocVerify", NetLingoXtra::m_cacheDocVerify, -1, 0, 500, HBLTIN },
+	{ "proxyServer", NetLingoXtra::m_proxyServer, -1, 0, 500, HBLTIN },
+	{ "browserName", NetLingoXtra::m_browserName, -1, 0, 500, HBLTIN },
+	{ "tellStreamStatus", NetLingoXtra::m_tellStreamStatus, -1, 0, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+NetLingoXtraObject::NetLingoXtraObject(ObjectType ObjectType) :Object<NetLingoXtraObject>("NetLingo") {
+	_objType = ObjectType;
+}
+
+bool NetLingoXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum NetLingoXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(NetLingoXtra::xlibName);
+	warning("NetLingoXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void NetLingoXtra::open(ObjectType type, const Common::Path &path) {
+    NetLingoXtraObject::initMethods(xlibMethods);
+    NetLingoXtraObject *xobj = new NetLingoXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void NetLingoXtra::close(ObjectType type) {
+    NetLingoXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void NetLingoXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("NetLingoXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUB(NetLingoXtra::m_netStatus, 0)
+XOBJSTUB(NetLingoXtra::m_getNetText, 0)
+XOBJSTUB(NetLingoXtra::m_gotoNetMovie, 0)
+XOBJSTUB(NetLingoXtra::m_preloadNetThing, 0)
+XOBJSTUB(NetLingoXtra::m_netAbort, 0)
+XOBJSTUB(NetLingoXtra::m_gotoNetPage, 0)
+XOBJSTUB(NetLingoXtra::m_getLatestNetID, 0)
+XOBJSTUB(NetLingoXtra::m_netError, "OK")
+XOBJSTUB(NetLingoXtra::m_netDone, 1)
+XOBJSTUB(NetLingoXtra::m_netTextResult, "")
+XOBJSTUB(NetLingoXtra::m_netMime, 0)
+XOBJSTUB(NetLingoXtra::m_netLastModDate, 0)
+XOBJSTUB(NetLingoXtra::m_externalEvent, 0)
+XOBJSTUB(NetLingoXtra::m_netPresent, 0)
+XOBJSTUB(NetLingoXtra::m_downloadNetThing, 0)
+XOBJSTUB(NetLingoXtra::m_clearCache, 0)
+XOBJSTUB(NetLingoXtra::m_cacheSize, 0)
+XOBJSTUB(NetLingoXtra::m_cacheDocVerify, 0)
+XOBJSTUB(NetLingoXtra::m_proxyServer, 0)
+XOBJSTUB(NetLingoXtra::m_browserName, "")
+XOBJSTUB(NetLingoXtra::m_tellStreamStatus, 0)
+
+}
diff --git a/engines/director/lingo/xtras/n/netlingo.h b/engines/director/lingo/xtras/n/netlingo.h
new file mode 100644
index 00000000000..3a51f952a5c
--- /dev/null
+++ b/engines/director/lingo/xtras/n/netlingo.h
@@ -0,0 +1,70 @@
+/* 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 DIRECTOR_LINGO_XTRAS_N_NETLINGO_H
+#define DIRECTOR_LINGO_XTRAS_N_NETLINGO_H
+
+namespace Director {
+
+class NetLingoXtraObject : public Object<NetLingoXtraObject> {
+public:
+	NetLingoXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace NetLingoXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_netStatus(int nargs);
+void m_getNetText(int nargs);
+void m_gotoNetMovie(int nargs);
+void m_preloadNetThing(int nargs);
+void m_netAbort(int nargs);
+void m_gotoNetPage(int nargs);
+void m_getLatestNetID(int nargs);
+void m_netError(int nargs);
+void m_netDone(int nargs);
+void m_netTextResult(int nargs);
+void m_netMime(int nargs);
+void m_netLastModDate(int nargs);
+void m_externalEvent(int nargs);
+void m_netPresent(int nargs);
+void m_downloadNetThing(int nargs);
+void m_clearCache(int nargs);
+void m_cacheSize(int nargs);
+void m_cacheDocVerify(int nargs);
+void m_proxyServer(int nargs);
+void m_browserName(int nargs);
+void m_tellStreamStatus(int nargs);
+
+} // End of namespace NetLingoXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index c7d636fa74e..cffea9f447b 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -198,6 +198,7 @@ MODULE_OBJS = \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
 	lingo/xtras/d/datetime.o \
+	lingo/xtras/n/netlingo.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \


Commit: 9034771ccfb6d7675925f830d74c26f4ea62f399
    https://github.com/scummvm/scummvm/commit/9034771ccfb6d7675925f830d74c26f4ea62f399
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub PaintX Xtra for Löwenzahn 2

Changed paths:
  A engines/director/lingo/xtras/p/paintx.cpp
  A engines/director/lingo/xtras/p/paintx.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index ae9ff02f3b1..663560aa311 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -169,6 +169,7 @@
 #include "director/lingo/xtras/n/netlingo.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
+#include "director/lingo/xtras/p/paintx.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
 #include "director/lingo/xtras/r/registryreader.h"
 #include "director/lingo/xtras/r/rtk.h"
@@ -349,6 +350,7 @@ static const struct XLibProto {
 	XLIBDEF(OpenURLXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(OrthoPlayXObj,		kXObj,			400),	// D4
 	XLIBDEF(PACoXObj,			kXObj,			300),	// D3
+	XLIBDEF(PaintXXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(PalXObj,			kXObj,			400),	// D4
 	XLIBDEF(PanelXObj,			kXObj,			200),	// D2
 	XLIBDEF(PharaohsXObj,		kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/p/paintx.cpp b/engines/director/lingo/xtras/p/paintx.cpp
new file mode 100644
index 00000000000..d3fbba2c8f6
--- /dev/null
+++ b/engines/director/lingo/xtras/p/paintx.cpp
@@ -0,0 +1,163 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/p/paintx.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2
+ *
+ **************************************************/
+
+/*
+-- xtra PaintX    -- Version 1.0 (WIN/32 Bit)
+
+-- --------------------------------------------------------------------
+-- Painting Xtra
+-- --------------------------------------------------------------------
+
+-- �1998 by Stephan Eichhorn, Scirius Development
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+-- special version for <Loewenzahn 2> only
+-- --------------------------------------------------------------------
+new object me                           -- create a new instance
+-- Methods --
+setRunMode object me, integer Mode      -- set the runMode (1,2,3)
+           -- 1: normal
+           -- 2: framed
+           -- 3: mirrored
+getRunMode object me                    -- returns the runMode
+setRunState object me, integer State    -- set the runState (0,1)
+           -- 0: stopped
+           -- 1: running
+getRunState object me                   -- returns the runState
+setData object me, string Data          -- set the session data
+getPos object me                        -- get position in data
+getDataSize object me                   -- get the size of data
+SetDrawRect object me, rect DrawRect    -- set the drawing rectangle
+SetClipRect object me, rect ClipRect    -- set the clipping rectangle
+Draw object me                          -- draw the next stroke
+StageToCast object me, rect CaptureRect, integer MemberNum  
+StageToBuffer object me, rect CaptureRect
+BufferToStage object me, rect DestRect
+setFile object me, string filePath      -- reads file as data
+Paint object me, integer Pensize, integer R, integer G, integer B, integer SL, integer ST
+
+ */
+
+namespace Director {
+
+const char *PaintXXtra::xlibName = "PaintX";
+const XlibFileDesc PaintXXtra::fileNames[] = {
+	{ "paintx",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				PaintXXtra::m_new,		 0, 0,	500 },
+	{ "setRunMode",				PaintXXtra::m_setRunMode,		 1, 1,	500 },
+	{ "getRunMode",				PaintXXtra::m_getRunMode,		 0, 0,	500 },
+	{ "setRunState",				PaintXXtra::m_setRunState,		 1, 1,	500 },
+	{ "getRunState",				PaintXXtra::m_getRunState,		 0, 0,	500 },
+	{ "setData",				PaintXXtra::m_setData,		 1, 1,	500 },
+	{ "getPos",				PaintXXtra::m_getPos,		 0, 0,	500 },
+	{ "getDataSize",				PaintXXtra::m_getDataSize,		 0, 0,	500 },
+	{ "SetDrawRect",				PaintXXtra::m_SetDrawRect,		 1, 1,	500 },
+	{ "SetClipRect",				PaintXXtra::m_SetClipRect,		 1, 1,	500 },
+	{ "Draw",				PaintXXtra::m_Draw,		 0, 0,	500 },
+	{ "StageToCast",				PaintXXtra::m_StageToCast,		 2, 2,	500 },
+	{ "StageToBuffer",				PaintXXtra::m_StageToBuffer,		 1, 1,	500 },
+	{ "BufferToStage",				PaintXXtra::m_BufferToStage,		 1, 1,	500 },
+	{ "setFile",				PaintXXtra::m_setFile,		 1, 1,	500 },
+	{ "Paint",				PaintXXtra::m_Paint,		 6, 6,	500 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+PaintXXtraObject::PaintXXtraObject(ObjectType ObjectType) :Object<PaintXXtraObject>("PaintX") {
+	_objType = ObjectType;
+}
+
+bool PaintXXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum PaintXXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(PaintXXtra::xlibName);
+	warning("PaintXXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void PaintXXtra::open(ObjectType type, const Common::Path &path) {
+    PaintXXtraObject::initMethods(xlibMethods);
+    PaintXXtraObject *xobj = new PaintXXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void PaintXXtra::close(ObjectType type) {
+    PaintXXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void PaintXXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("PaintXXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUB(PaintXXtra::m_setRunMode, 0)
+XOBJSTUB(PaintXXtra::m_getRunMode, 0)
+XOBJSTUB(PaintXXtra::m_setRunState, 0)
+XOBJSTUB(PaintXXtra::m_getRunState, 0)
+XOBJSTUB(PaintXXtra::m_setData, 0)
+XOBJSTUB(PaintXXtra::m_getPos, 0)
+XOBJSTUB(PaintXXtra::m_getDataSize, 0)
+XOBJSTUB(PaintXXtra::m_SetDrawRect, 0)
+XOBJSTUB(PaintXXtra::m_SetClipRect, 0)
+XOBJSTUB(PaintXXtra::m_Draw, 0)
+XOBJSTUB(PaintXXtra::m_StageToCast, 0)
+XOBJSTUB(PaintXXtra::m_StageToBuffer, 0)
+XOBJSTUB(PaintXXtra::m_BufferToStage, 0)
+XOBJSTUB(PaintXXtra::m_setFile, 0)
+XOBJSTUB(PaintXXtra::m_Paint, 0)
+
+}
diff --git a/engines/director/lingo/xtras/p/paintx.h b/engines/director/lingo/xtras/p/paintx.h
new file mode 100644
index 00000000000..9e62bd0b7c8
--- /dev/null
+++ b/engines/director/lingo/xtras/p/paintx.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 DIRECTOR_LINGO_XTRAS_P_PAINTX_H
+#define DIRECTOR_LINGO_XTRAS_P_PAINTX_H
+
+namespace Director {
+
+class PaintXXtraObject : public Object<PaintXXtraObject> {
+public:
+	PaintXXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace PaintXXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_setRunMode(int nargs);
+void m_getRunMode(int nargs);
+void m_setRunState(int nargs);
+void m_getRunState(int nargs);
+void m_setData(int nargs);
+void m_getPos(int nargs);
+void m_getDataSize(int nargs);
+void m_SetDrawRect(int nargs);
+void m_SetClipRect(int nargs);
+void m_Draw(int nargs);
+void m_StageToCast(int nargs);
+void m_StageToBuffer(int nargs);
+void m_BufferToStage(int nargs);
+void m_setFile(int nargs);
+void m_Paint(int nargs);
+
+} // End of namespace PaintXXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index cffea9f447b..80ec539cc47 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -201,6 +201,7 @@ MODULE_OBJS = \
 	lingo/xtras/n/netlingo.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
+	lingo/xtras/p/paintx.o \
 	lingo/xtras/q/qtvrxtra.o \
 	lingo/xtras/r/registryreader.o \
 	lingo/xtras/r/rtk.o \


Commit: c79750df71330618eebb442ba992348ebd658ac6
    https://github.com/scummvm/scummvm/commit/c79750df71330618eebb442ba992348ebd658ac6
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub SetMouse (SMXTRA) Xtra for Löwenzahn 2-4

Changed paths:
  A engines/director/lingo/xtras/s/setmouse.cpp
  A engines/director/lingo/xtras/s/setmouse.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 663560aa311..23290f1d8dc 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -181,6 +181,7 @@
 #include "director/lingo/xtras/s/staytoonedhigh.h"
 #include "director/lingo/xtras/s/staytoonedober.h"
 #include "director/lingo/xtras/s/staytoonedtoon.h"
+#include "director/lingo/xtras/s/setmouse.h"
 #include "director/lingo/xtras/t/timextra.h"
 #include "director/lingo/xtras/x/xsound.h"
 
@@ -375,6 +376,7 @@ static const struct XLibProto {
 	XLIBDEF(SaveNRestoreXObj,			kXObj,					400),	// D4
 	XLIBDEF(ScrnUtilXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(SerialPortXObj,		kXObj,			200),	// D2
+	XLIBDEF(SetMouseXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(SmackerXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(SmallUtilXObj,		kXObj,			400),	// D4
 	XLIBDEF(SoundJam,			kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/s/setmouse.cpp b/engines/director/lingo/xtras/s/setmouse.cpp
new file mode 100644
index 00000000000..987e5775514
--- /dev/null
+++ b/engines/director/lingo/xtras/s/setmouse.cpp
@@ -0,0 +1,125 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/s/setmouse.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2 / 3
+ *
+ **************************************************/
+
+/*
+-- xtra SetMouseXtra               -- Version 1.0
+-- --------------------------------------------------------------------
+-- this Xtra allows to set the mouse position through Lingo
+-- --------------------------------------------------------------------
+
+-- �1997 by Stephan Eichhorn, Scirius Multimedia
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+* SetMouse integer x,integer y     -- set the cursor to position x,y 
+-- 
+-- all x,y are in screen coordinates,
+-- not relative to the stage! 
+-- 
+
+ */
+
+namespace Director {
+
+const char *SetMouseXtra::xlibName = "SetMouse";
+const XlibFileDesc SetMouseXtra::fileNames[] = {
+	{ "SMXTRA",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "SetMouse", SetMouseXtra::m_SetMouse, 2, 2, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+SetMouseXtraObject::SetMouseXtraObject(ObjectType ObjectType) :Object<SetMouseXtraObject>("SetMouse") {
+	_objType = ObjectType;
+}
+
+bool SetMouseXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum SetMouseXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(SetMouseXtra::xlibName);
+	warning("SetMouseXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void SetMouseXtra::open(ObjectType type, const Common::Path &path) {
+    SetMouseXtraObject::initMethods(xlibMethods);
+    SetMouseXtraObject *xobj = new SetMouseXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void SetMouseXtra::close(ObjectType type) {
+    SetMouseXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void SetMouseXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("SetMouseXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void SetMouseXtra::m_SetMouse(int nargs) {
+	if (nargs != 2) {
+		warning("SetMouseXtra::m_SetMouse: expected 2 arguments, got %d", nargs);
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(0));
+		return;
+	}
+	int y = g_lingo->pop().asInt();
+	int x = g_lingo->pop().asInt();
+	g_system->warpMouse(x, y);
+	g_lingo->push(Datum(0));
+}
+
+}
diff --git a/engines/director/lingo/xtras/s/setmouse.h b/engines/director/lingo/xtras/s/setmouse.h
new file mode 100644
index 00000000000..22daf7a6e11
--- /dev/null
+++ b/engines/director/lingo/xtras/s/setmouse.h
@@ -0,0 +1,50 @@
+/* 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 DIRECTOR_LINGO_XTRAS_S_SETMOUSE_H
+#define DIRECTOR_LINGO_XTRAS_S_SETMOUSE_H
+
+namespace Director {
+
+class SetMouseXtraObject : public Object<SetMouseXtraObject> {
+public:
+	SetMouseXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace SetMouseXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_SetMouse(int nargs);
+
+} // End of namespace SetMouseXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 80ec539cc47..be7bbdbbf47 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -202,6 +202,7 @@ MODULE_OBJS = \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/p/paintx.o \
+	lingo/xtras/s/setmouse.o \
 	lingo/xtras/q/qtvrxtra.o \
 	lingo/xtras/r/registryreader.o \
 	lingo/xtras/r/rtk.o \


Commit: 846ab92664d9a96dd3f9599c370b8e81eb4c511f
    https://github.com/scummvm/scummvm/commit/846ab92664d9a96dd3f9599c370b8e81eb4c511f
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Implement FileXtra4

FileXtra4 is used by TKKG 11/13/14, the Loewenzahn 1 D10 reissue
and Oscar Mountain for their cd-checks.

Changed paths:
  A engines/director/lingo/xtras/f/filextra4.cpp
  A engines/director/lingo/xtras/f/filextra4.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 23290f1d8dc..50c5803071b 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -162,6 +162,7 @@
 #include "director/lingo/xtras/d/displayres.h"
 #include "director/lingo/xtras/d/datetime.h"
 #include "director/lingo/xtras/f/filextra.h"
+#include "director/lingo/xtras/f/filextra4.h"
 #include "director/lingo/xtras/g/getdir.h"
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
@@ -300,6 +301,7 @@ static const struct XLibProto {
 	XLIBDEF(FileExists,			kXObj,			300),	// D3
 	XLIBDEF(FileIO,				kXObj | kXtraObj,200),	// D2
 	XLIBDEF(FileXtra,			kXtraObj,		500),	// D5
+	XLIBDEF(FileXtra4Xtra,			kXtraObj,					500),	// D5
 	XLIBDEF(FindFolder,			kXObj,			300),	// D3
 	XLIBDEF(FindSys,			kXObj,			400),	// D4
 	XLIBDEF(FindWin,			kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/f/filextra4.cpp b/engines/director/lingo/xtras/f/filextra4.cpp
new file mode 100644
index 00000000000..b6a94cecb5b
--- /dev/null
+++ b/engines/director/lingo/xtras/f/filextra4.cpp
@@ -0,0 +1,342 @@
+/* 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/system.h"
+#include "common/fs.h"
+
+#include "director/director.h"
+#include "director/util.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/f/filextra4.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 1 (D10, v2.0)
+ * TKKG 11 / 13 / 14
+ * Oscar the Balloonist Flies into the Mountains
+ *
+ **************************************************/
+
+/*
+-- xtra FileXtra4
+new object me
+fx_GetVersion object me
+fx_FileOpenDialog object me, string initialFolder, string filtStr, string dlogTitle, Boolean createPrompt, Boolean fileMustExist
+fx_FileSaveAsDialog object me, string initialFolder, string filename, string dlogTitle, Boolean overwritePrompt
+fx_FileExists object me, string fileName
+fx_FileIsLink object me, string fileName
+fx_FileRename object me, string oldName, string newName
+fx_FileDelete object me, string fileName
+fx_FileRecycle object me, string fileName
+fx_FileCopy object me, string fromFName, string toFName
+fx_FileMove object me, string fromFName, string toFName
+fx_FileGetWriteState object me, string fileName
+fx_FileSetWriteState object me, string fileName, Boolean writeable
+fx_FileGetModDate object me, string fileName
+fx_FileGetModNumber object me, string fileName
+fx_FileGetSize object me, string fileName
+fx_FileGetType object me, string fileName
+fx_FileSetType object me, string fileName, string fileType
+fx_FileCompare object me, string filename1, string filename2
+fx_FileOpenDocument object me, string filename
+fx_FilePrintDocument object me, string filename
+fx_FileGetAppPath object me, string fileType
+fx_FileRunApp object me, string commandLine
+fx_LinkCreate object me, string filename, string destPath
+fx_LinkResolve object me, string filename
+fx_FolderSelectDialog object me, string infoString
+fx_FolderGetSpecialPath object me, string folderType
+fx_FolderExists object me, string folderName
+fx_FolderCreate object me, string folderName
+fx_FolderRename object me, string oldFolderName, string newFolderName
+fx_FolderDelete object me, string folderName, Boolean recursive
+fx_FolderRecycle object me, string folderName
+fx_FolderCopy object me, string fromFolderName, string toFolderName, Boolean recursive
+fx_FolderMove object me, string fromFolderName, string toFolderName
+fx_FolderGetWriteState object me, string folderName
+fx_FolderSetWriteState object me, string folderName, Boolean writeable, Boolean recursive
+fx_FolderSyncOneWay object me, string fromFolderName, string toFolderName, Boolean recursive, Boolean deleteStrays
+fx_FolderSyncBothWays object me, string fromFolderName, string toFolderName, Boolean recursive
+fx_FolderToList object me, string folderName
+fx_VolumeSelectDialog object me, string infoString
+fx_VolumeExists object me, string volumeName
+fx_VolumeGetFreeBytes object me, string volumeName
+fx_VolumeGetTotalBytes object me, string volumeName
+fx_VolumeIsCDROM object me, string volumeName
+fx_VolumeIsRemovable object me, string volumeName
+fx_VolumeEject object me, string volumeName
+fx_VolumesToList object me
+fx_ErrorNumber object me
+fx_ErrorString object me
+
+ */
+
+namespace Director {
+
+const char *FileXtra4Xtra::xlibName = "FileXtra4";
+const XlibFileDesc FileXtra4Xtra::fileNames[] = {
+	{ "filextra4",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				FileXtra4Xtra::m_new,		 0, 0,	500 },
+	{ "fx_GetVersion",				FileXtra4Xtra::m_fx_GetVersion,		 0, 0,	500 },
+	{ "fx_FileOpenDialog",				FileXtra4Xtra::m_fx_FileOpenDialog,		 5, 5,	500 },
+	{ "fx_FileSaveAsDialog",				FileXtra4Xtra::m_fx_FileSaveAsDialog,		 4, 4,	500 },
+	{ "fx_FileExists",				FileXtra4Xtra::m_fx_FileExists,		 1, 1,	500 },
+	{ "fx_FileIsLink",				FileXtra4Xtra::m_fx_FileIsLink,		 1, 1,	500 },
+	{ "fx_FileRename",				FileXtra4Xtra::m_fx_FileRename,		 2, 2,	500 },
+	{ "fx_FileDelete",				FileXtra4Xtra::m_fx_FileDelete,		 1, 1,	500 },
+	{ "fx_FileRecycle",				FileXtra4Xtra::m_fx_FileRecycle,		 1, 1,	500 },
+	{ "fx_FileCopy",				FileXtra4Xtra::m_fx_FileCopy,		 2, 2,	500 },
+	{ "fx_FileMove",				FileXtra4Xtra::m_fx_FileMove,		 2, 2,	500 },
+	{ "fx_FileGetWriteState",				FileXtra4Xtra::m_fx_FileGetWriteState,		 1, 1,	500 },
+	{ "fx_FileSetWriteState",				FileXtra4Xtra::m_fx_FileSetWriteState,		 2, 2,	500 },
+	{ "fx_FileGetModDate",				FileXtra4Xtra::m_fx_FileGetModDate,		 1, 1,	500 },
+	{ "fx_FileGetModNumber",				FileXtra4Xtra::m_fx_FileGetModNumber,		 1, 1,	500 },
+	{ "fx_FileGetSize",				FileXtra4Xtra::m_fx_FileGetSize,		 1, 1,	500 },
+	{ "fx_FileGetType",				FileXtra4Xtra::m_fx_FileGetType,		 1, 1,	500 },
+	{ "fx_FileSetType",				FileXtra4Xtra::m_fx_FileSetType,		 2, 2,	500 },
+	{ "fx_FileCompare",				FileXtra4Xtra::m_fx_FileCompare,		 2, 2,	500 },
+	{ "fx_FileOpenDocument",				FileXtra4Xtra::m_fx_FileOpenDocument,		 1, 1,	500 },
+	{ "fx_FilePrintDocument",				FileXtra4Xtra::m_fx_FilePrintDocument,		 1, 1,	500 },
+	{ "fx_FileGetAppPath",				FileXtra4Xtra::m_fx_FileGetAppPath,		 1, 1,	500 },
+	{ "fx_FileRunApp",				FileXtra4Xtra::m_fx_FileRunApp,		 1, 1,	500 },
+	{ "fx_LinkCreate",				FileXtra4Xtra::m_fx_LinkCreate,		 2, 2,	500 },
+	{ "fx_LinkResolve",				FileXtra4Xtra::m_fx_LinkResolve,		 1, 1,	500 },
+	{ "fx_FolderSelectDialog",				FileXtra4Xtra::m_fx_FolderSelectDialog,		 1, 1,	500 },
+	{ "fx_FolderGetSpecialPath",				FileXtra4Xtra::m_fx_FolderGetSpecialPath,		 1, 1,	500 },
+	{ "fx_FolderExists",				FileXtra4Xtra::m_fx_FolderExists,		 1, 1,	500 },
+	{ "fx_FolderCreate",				FileXtra4Xtra::m_fx_FolderCreate,		 1, 1,	500 },
+	{ "fx_FolderRename",				FileXtra4Xtra::m_fx_FolderRename,		 2, 2,	500 },
+	{ "fx_FolderDelete",				FileXtra4Xtra::m_fx_FolderDelete,		 2, 2,	500 },
+	{ "fx_FolderRecycle",				FileXtra4Xtra::m_fx_FolderRecycle,		 1, 1,	500 },
+	{ "fx_FolderCopy",				FileXtra4Xtra::m_fx_FolderCopy,		 3, 3,	500 },
+	{ "fx_FolderMove",				FileXtra4Xtra::m_fx_FolderMove,		 2, 2,	500 },
+	{ "fx_FolderGetWriteState",				FileXtra4Xtra::m_fx_FolderGetWriteState,		 1, 1,	500 },
+	{ "fx_FolderSetWriteState",				FileXtra4Xtra::m_fx_FolderSetWriteState,		 3, 3,	500 },
+	{ "fx_FolderSyncOneWay",				FileXtra4Xtra::m_fx_FolderSyncOneWay,		 4, 4,	500 },
+	{ "fx_FolderSyncBothWays",				FileXtra4Xtra::m_fx_FolderSyncBothWays,		 3, 3,	500 },
+	{ "fx_FolderToList",				FileXtra4Xtra::m_fx_FolderToList,		 1, 1,	500 },
+	{ "fx_VolumeSelectDialog",				FileXtra4Xtra::m_fx_VolumeSelectDialog,		 1, 1,	500 },
+	{ "fx_VolumeExists",				FileXtra4Xtra::m_fx_VolumeExists,		 1, 1,	500 },
+	{ "fx_VolumeGetFreeBytes",				FileXtra4Xtra::m_fx_VolumeGetFreeBytes,		 1, 1,	500 },
+	{ "fx_VolumeGetTotalBytes",				FileXtra4Xtra::m_fx_VolumeGetTotalBytes,		 1, 1,	500 },
+	{ "fx_VolumeIsCDROM",				FileXtra4Xtra::m_fx_VolumeIsCDROM,		 1, 1,	500 },
+	{ "fx_VolumeIsRemovable",				FileXtra4Xtra::m_fx_VolumeIsRemovable,		 1, 1,	500 },
+	{ "fx_VolumeEject",				FileXtra4Xtra::m_fx_VolumeEject,		 1, 1,	500 },
+	{ "fx_VolumesToList",				FileXtra4Xtra::m_fx_VolumesToList,		 0, 0,	500 },
+	{ "fx_ErrorNumber",				FileXtra4Xtra::m_fx_ErrorNumber,		 0, 0,	500 },
+	{ "fx_ErrorString",				FileXtra4Xtra::m_fx_ErrorString,		 0, 0,	500 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+FileXtra4XtraObject::FileXtra4XtraObject(ObjectType ObjectType) :Object<FileXtra4XtraObject>("FileXtra4") {
+	_objType = ObjectType;
+}
+
+bool FileXtra4XtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum FileXtra4XtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(FileXtra4Xtra::xlibName);
+	warning("FileXtra4Xtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void FileXtra4Xtra::open(ObjectType type, const Common::Path &path) {
+    FileXtra4XtraObject::initMethods(xlibMethods);
+    FileXtra4XtraObject *xobj = new FileXtra4XtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void FileXtra4Xtra::close(ObjectType type) {
+    FileXtra4XtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void FileXtra4Xtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("FileXtra4Xtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+static Common::String fx4FirstArg(int nargs) {
+	Common::String first;
+	for (int i = 0; i < nargs; i++) {
+		Datum d = g_lingo->pop();
+		if (i == nargs - 1)
+			first = d.asString();
+	}
+	return first;
+}
+
+static Common::Path fx4ResolveAbs(const Common::String &raw, bool directory) {
+	Common::Path rel = findPath(raw, true, true, directory);
+	if (rel.empty())
+		return Common::Path();
+	Common::Path abs = Common::Path(g_director->getGameDataDir()->getPath());
+	abs.appendInPlace(Common::String(g_director->_dirSeparator), g_director->_dirSeparator);
+	abs.appendInPlace(rel);
+	return abs;
+}
+
+void FileXtra4Xtra::m_fx_GetVersion(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(Common::String("FileXtra 4.0.2 (ScummVM)")));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileOpenDialog, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSaveAsDialog, 0)
+void FileXtra4Xtra::m_fx_FileExists(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	g_lingo->push(Datum(findPath(name, true, true, false).empty() ? 0 : 1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileIsLink, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRename, 1)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileDelete, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRecycle, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileCopy, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileMove, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetModDate, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetModNumber, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetSize, 0)
+void FileXtra4Xtra::m_fx_FileGetType(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	Common::String ext;
+	for (int i = (int)name.size() - 1; i >= 0; i--) {
+		char c = name[i];
+		if (c == '\\' || c == '/' || c == ':')
+			break;
+		if (c == '.') {
+			ext = Common::String(name.c_str() + i);
+			break;
+		}
+	}
+	g_lingo->push(Datum(ext));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSetType, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileCompare, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileOpenDocument, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FilePrintDocument, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetAppPath, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRunApp, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_LinkCreate, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_LinkResolve, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSelectDialog, 0)
+void FileXtra4Xtra::m_fx_FolderGetSpecialPath(int nargs) {
+	g_lingo->dropStack(nargs);
+	Common::Path base = g_director->getGameDataDir()->getPath();
+	Common::String path = base.toString(g_director->_dirSeparator);
+	if (!path.empty() && path.lastChar() != g_director->_dirSeparator)
+		path += g_director->_dirSeparator;
+	g_lingo->push(Datum(path));
+}
+void FileXtra4Xtra::m_fx_FolderExists(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	g_lingo->push(Datum(findPath(name, true, true, true).empty() ? 0 : 1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderCreate, 1)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderRename, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderDelete, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderRecycle, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderCopy, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderMove, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderGetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSyncOneWay, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSyncBothWays, 0)
+void FileXtra4Xtra::m_fx_FolderToList(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+
+	Datum result;
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+
+	Common::Path dirPath = fx4ResolveAbs(name, true);
+	if (!dirPath.empty()) {
+		Common::FSNode dir(dirPath);
+		Common::FSList fslist;
+		if (dir.isDirectory() && dir.getChildren(fslist, Common::FSNode::kListAll)) {
+			for (auto &node : fslist) {
+				Common::String childName = node.getName();
+				if (node.isDirectory())
+					childName += g_director->_dirSeparator;
+				result.u.farr->arr.push_back(Datum(childName));
+			}
+		}
+	}
+	g_lingo->push(result);
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeSelectDialog, 0)
+void FileXtra4Xtra::m_fx_VolumeExists(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeGetFreeBytes, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeGetTotalBytes, 0)
+void FileXtra4Xtra::m_fx_VolumeIsCDROM(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(0));
+}
+void FileXtra4Xtra::m_fx_VolumeIsRemovable(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeEject, 0)
+void FileXtra4Xtra::m_fx_VolumesToList(int nargs) {
+	g_lingo->dropStack(nargs);
+	Datum result;
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+	result.u.farr->arr.push_back(Datum(Common::String("c:\\")));
+	g_lingo->push(result);
+}
+void FileXtra4Xtra::m_fx_ErrorNumber(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(0));
+}
+
+void FileXtra4Xtra::m_fx_ErrorString(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(Common::String("")));
+}
+
+}
diff --git a/engines/director/lingo/xtras/f/filextra4.h b/engines/director/lingo/xtras/f/filextra4.h
new file mode 100644
index 00000000000..eee09832b52
--- /dev/null
+++ b/engines/director/lingo/xtras/f/filextra4.h
@@ -0,0 +1,97 @@
+/* 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 DIRECTOR_LINGO_XTRAS_F_FILEXTRA4_H
+#define DIRECTOR_LINGO_XTRAS_F_FILEXTRA4_H
+
+namespace Director {
+
+class FileXtra4XtraObject : public Object<FileXtra4XtraObject> {
+public:
+	FileXtra4XtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace FileXtra4Xtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_fx_GetVersion(int nargs);
+void m_fx_FileOpenDialog(int nargs);
+void m_fx_FileSaveAsDialog(int nargs);
+void m_fx_FileExists(int nargs);
+void m_fx_FileIsLink(int nargs);
+void m_fx_FileRename(int nargs);
+void m_fx_FileDelete(int nargs);
+void m_fx_FileRecycle(int nargs);
+void m_fx_FileCopy(int nargs);
+void m_fx_FileMove(int nargs);
+void m_fx_FileGetWriteState(int nargs);
+void m_fx_FileSetWriteState(int nargs);
+void m_fx_FileGetModDate(int nargs);
+void m_fx_FileGetModNumber(int nargs);
+void m_fx_FileGetSize(int nargs);
+void m_fx_FileGetType(int nargs);
+void m_fx_FileSetType(int nargs);
+void m_fx_FileCompare(int nargs);
+void m_fx_FileOpenDocument(int nargs);
+void m_fx_FilePrintDocument(int nargs);
+void m_fx_FileGetAppPath(int nargs);
+void m_fx_FileRunApp(int nargs);
+void m_fx_LinkCreate(int nargs);
+void m_fx_LinkResolve(int nargs);
+void m_fx_FolderSelectDialog(int nargs);
+void m_fx_FolderGetSpecialPath(int nargs);
+void m_fx_FolderExists(int nargs);
+void m_fx_FolderCreate(int nargs);
+void m_fx_FolderRename(int nargs);
+void m_fx_FolderDelete(int nargs);
+void m_fx_FolderRecycle(int nargs);
+void m_fx_FolderCopy(int nargs);
+void m_fx_FolderMove(int nargs);
+void m_fx_FolderGetWriteState(int nargs);
+void m_fx_FolderSetWriteState(int nargs);
+void m_fx_FolderSyncOneWay(int nargs);
+void m_fx_FolderSyncBothWays(int nargs);
+void m_fx_FolderToList(int nargs);
+void m_fx_VolumeSelectDialog(int nargs);
+void m_fx_VolumeExists(int nargs);
+void m_fx_VolumeGetFreeBytes(int nargs);
+void m_fx_VolumeGetTotalBytes(int nargs);
+void m_fx_VolumeIsCDROM(int nargs);
+void m_fx_VolumeIsRemovable(int nargs);
+void m_fx_VolumeEject(int nargs);
+void m_fx_VolumesToList(int nargs);
+void m_fx_ErrorNumber(int nargs);
+void m_fx_ErrorString(int nargs);
+
+} // End of namespace FileXtra4Xtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index be7bbdbbf47..f2ce92bd4d3 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -199,6 +199,7 @@ MODULE_OBJS = \
 	lingo/xtras/m/mui.o \
 	lingo/xtras/d/datetime.o \
 	lingo/xtras/n/netlingo.o \
+	lingo/xtras/f/filextra4.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/p/paintx.o \


Commit: d535c2f9a74d112f0d8ab8ea603641e3f2ba80c1
    https://github.com/scummvm/scummvm/commit/d535c2f9a74d112f0d8ab8ea603641e3f2ba80c1
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Implement FileXtra DirectoryExists and DirectoryToList

DirectoryExists returns 0 if the directory exists and a negative error code
otherwise, matching the original FileXtra convention. DirectoryToList returns
a Lingo list of the entries in a directory or VOID for an invalid directory.

Changed paths:
    engines/director/lingo/xtras/f/filextra.cpp


diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 2531ea12a89..15ebd234e40 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -20,8 +20,10 @@
  */
 
 #include "common/system.h"
+#include "common/fs.h"
 
 #include "director/director.h"
+#include "director/util.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingo-utils.h"
@@ -153,12 +155,57 @@ XOBJSTUB(FileXtra::m_RenameFile, 0)
 XOBJSTUB(FileXtra::m_DeleteFile, 0)
 XOBJSTUB(FileXtra::m_CopyFile, 0)
 XOBJSTUB(FileXtra::m_GetFileModDate, 0)
-XOBJSTUB(FileXtra::m_DirectoryExists, 0)
+// DirectoryExists string dirName
+// FileXtra convention: returns 0 if the directory exists, a negative error
+// code otherwise.
+void FileXtra::m_DirectoryExists(int nargs) {
+	ARGNUMCHECK(1)
+	Common::String dirName = g_lingo->pop().asString();
+
+	Common::Path path = findPath(dirName, true, true, true);
+	g_lingo->push(Datum(path.empty() ? -1 : 0));
+}
+
+// DirectoryToList string dirName
+// Returns a Lingo list of the files and folders in dirName. Folder names are
+// suffixed with the platform path delimiter. Returns VOID if dirName is not a
+// valid directory (matching the original FileXtra behaviour).
+void FileXtra::m_DirectoryToList(int nargs) {
+	ARGNUMCHECK(1)
+	Common::String dirName = g_lingo->pop().asString();
+
+	Datum result;
+
+	Common::Path path = findPath(dirName, true, true, true);
+	if (path.empty()) {
+		warning("FileXtra::m_DirectoryToList(): directory not found: %s", dirName.c_str());
+		g_lingo->push(result);
+		return;
+	}
+
+	Common::FSNode dir(path);
+	Common::FSList fslist;
+	if (!dir.isDirectory() || !dir.getChildren(fslist, Common::FSNode::kListAll)) {
+		g_lingo->push(result);
+		return;
+	}
+
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+	for (auto &node : fslist) {
+		Common::String name = node.getName();
+		if (node.isDirectory())
+			name += g_director->_dirSeparator;
+		result.u.farr->arr.push_back(Datum(name));
+	}
+
+	g_lingo->push(result);
+}
+
 XOBJSTUB(FileXtra::m_CreateDirectory, 0)
 XOBJSTUB(FileXtra::m_DeleteDirectory, 0)
 XOBJSTUB(FileXtra::m_XDeleteDirectory, 0)
 XOBJSTUB(FileXtra::m_CopyDirectory, 0)
 XOBJSTUB(FileXtra::m_XCopyDirectory, 0)
-XOBJSTUB(FileXtra::m_DirectoryToList, 0)
 
 }


Commit: 4bb528d1a6d63a0ce311cecd2ad68e0871b8bae0
    https://github.com/scummvm/scummvm/commit/4bb528d1a6d63a0ce311cecd2ad68e0871b8bae0
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Load Xtras from nested folders

Director treats the Xtras folder as the one next to the application. For titles
whose projector is nested below the game root also match an "xtras"
folder at any depth.

Changed paths:
    engines/director/resource.cpp


diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp
index ca9e4961ffb..89ad17e2c71 100644
--- a/engines/director/resource.cpp
+++ b/engines/director/resource.cpp
@@ -605,6 +605,7 @@ void Window::loadXtrasFromPath() {
 	// and they'll still be recognized.
 	Common::ArchiveMemberList targets;
 	SearchMan.listMatchingMembers(targets, Common::Path("xtras/*"), true);
+	SearchMan.listMatchingMembers(targets, Common::Path("*/xtras/*"), false);
 	for (auto &it : targets) {
 		if (it->isDirectory())
 			continue;


Commit: a2f863f792884dc80ec8c1b0457f42b04cc5847f
    https://github.com/scummvm/scummvm/commit/a2f863f792884dc80ec8c1b0457f42b04cc5847f
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Recognise QTASSET as the QuickTime asset Xtra
Fixes Löwenzahn 2 & 3 as well as Oscar the Balloonist Drops into the Countryside

Changed paths:
    engines/director/lingo/xlibs/q/qtsupport.cpp


diff --git a/engines/director/lingo/xlibs/q/qtsupport.cpp b/engines/director/lingo/xlibs/q/qtsupport.cpp
index e440c5a2d9c..a1597237368 100644
--- a/engines/director/lingo/xlibs/q/qtsupport.cpp
+++ b/engines/director/lingo/xlibs/q/qtsupport.cpp
@@ -23,6 +23,8 @@
  *
  * USED IN:
  * The Legend of Lotus Spring
+ * Löwenzahn 2&3
+ * Oscar the Balloonist Drops into the Countryside
  *
  *************************************/
 
@@ -65,6 +67,7 @@ namespace Director {
 const char *QTSupport::xlibName = "QuickTimeSupport";
 const XlibFileDesc QTSupport::fileNames[] = {
 	{ "QuickTime Asset",	nullptr },
+	{ "QTASSET",			nullptr },
 	{ nullptr,		nullptr },
 };
 


Commit: e8c1920cfbc1c3feadb0313f92657a016c455966
    https://github.com/scummvm/scummvm/commit/e8c1920cfbc1c3feadb0313f92657a016c455966
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Fix FileXtra DirectoryToList path resolution

Resolve the FSNode against the game directory instead of the process working
directory, so the listing works regardless of where ScummVM was launched.

Fixes the Löwenzahn 3 cd-check

Changed paths:
    engines/director/lingo/xtras/f/filextra.cpp


diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 15ebd234e40..511a267c89f 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -33,6 +33,9 @@
  *
  * USED IN:
  * I Spy
+ * Loewenzahn 2 / 3 / 4 / 8 / Adventskalender
+ * TKKG 7 / 8 / 9 / 10
+ * Oscar the Balloonist Discovers the Sea
  *
  **************************************************/
 
@@ -155,9 +158,7 @@ XOBJSTUB(FileXtra::m_RenameFile, 0)
 XOBJSTUB(FileXtra::m_DeleteFile, 0)
 XOBJSTUB(FileXtra::m_CopyFile, 0)
 XOBJSTUB(FileXtra::m_GetFileModDate, 0)
-// DirectoryExists string dirName
-// FileXtra convention: returns 0 if the directory exists, a negative error
-// code otherwise.
+
 void FileXtra::m_DirectoryExists(int nargs) {
 	ARGNUMCHECK(1)
 	Common::String dirName = g_lingo->pop().asString();
@@ -166,10 +167,6 @@ void FileXtra::m_DirectoryExists(int nargs) {
 	g_lingo->push(Datum(path.empty() ? -1 : 0));
 }
 
-// DirectoryToList string dirName
-// Returns a Lingo list of the files and folders in dirName. Folder names are
-// suffixed with the platform path delimiter. Returns VOID if dirName is not a
-// valid directory (matching the original FileXtra behaviour).
 void FileXtra::m_DirectoryToList(int nargs) {
 	ARGNUMCHECK(1)
 	Common::String dirName = g_lingo->pop().asString();
@@ -183,7 +180,10 @@ void FileXtra::m_DirectoryToList(int nargs) {
 		return;
 	}
 
-	Common::FSNode dir(path);
+	Common::Path absPath = Common::Path(g_director->getGameDataDir()->getPath());
+	absPath.appendInPlace(Common::String(g_director->_dirSeparator), g_director->_dirSeparator);
+	absPath.appendInPlace(path);
+	Common::FSNode dir(absPath);
 	Common::FSList fslist;
 	if (!dir.isDirectory() || !dir.getChildren(fslist, Common::FSNode::kListAll)) {
 		g_lingo->push(result);


Commit: f216fd288060e14f8811629bd670588c5b128a0b
    https://github.com/scummvm/scummvm/commit/f216fd288060e14f8811629bd670588c5b128a0b
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: List German Terzio/Tivola titles in Xtra USED IN blocks

Record which Loewenzahn, TKKG and Oscar titles use each Xtra

Changed paths:
    engines/director/lingo/xtras/a/audio.cpp
    engines/director/lingo/xtras/b/budapi.cpp
    engines/director/lingo/xtras/d/directsound.cpp
    engines/director/lingo/xtras/f/filextra.cpp
    engines/director/lingo/xtras/k/keypoll.cpp
    engines/director/lingo/xtras/m/mui.cpp
    engines/director/lingo/xtras/r/rtk.cpp
    engines/director/lingo/xtras/s/setmouse.cpp


diff --git a/engines/director/lingo/xtras/a/audio.cpp b/engines/director/lingo/xtras/a/audio.cpp
index 96c0f711f22..396e6bea2d7 100644
--- a/engines/director/lingo/xtras/a/audio.cpp
+++ b/engines/director/lingo/xtras/a/audio.cpp
@@ -30,7 +30,7 @@
 /**************************************************
  *
  * USED IN:
- * [insert game here]
+ * TKKG 6 / 7
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/b/budapi.cpp b/engines/director/lingo/xtras/b/budapi.cpp
index a6246d05d4c..7db2e31a146 100644
--- a/engines/director/lingo/xtras/b/budapi.cpp
+++ b/engines/director/lingo/xtras/b/budapi.cpp
@@ -31,6 +31,9 @@
  *
  * USED IN:
  * I Spy Spooky Mansion
+ * Loewenzahn 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 6 / 7 / 8 / 9 / 11 / 13 / 14
+ * Oscar the Balloonist Flies into the Mountains
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/d/directsound.cpp b/engines/director/lingo/xtras/d/directsound.cpp
index bbc24f7e489..82c3f8559bf 100644
--- a/engines/director/lingo/xtras/d/directsound.cpp
+++ b/engines/director/lingo/xtras/d/directsound.cpp
@@ -33,6 +33,9 @@
  *
  * USED IN:
  * safecracker
+ * Loewenzahn 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 7 / 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist (Sea / Mountains)
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 511a267c89f..29c1a626fbc 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -33,7 +33,7 @@
  *
  * USED IN:
  * I Spy
- * Loewenzahn 2 / 3 / 4 / 8 / Adventskalender
+ * Loewenzahn 2 / 3 / 4 / 5 / 7 / 8 / Adventskalender / Spielebox
  * TKKG 7 / 8 / 9 / 10
  * Oscar the Balloonist Discovers the Sea
  *
diff --git a/engines/director/lingo/xtras/k/keypoll.cpp b/engines/director/lingo/xtras/k/keypoll.cpp
index f11da3e04cb..07468fd6cb2 100644
--- a/engines/director/lingo/xtras/k/keypoll.cpp
+++ b/engines/director/lingo/xtras/k/keypoll.cpp
@@ -32,6 +32,7 @@
  * USED IN:
  * Safecracker
  * Teazle
+ * Loewenzahn 2 / 4 / 5
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/m/mui.cpp b/engines/director/lingo/xtras/m/mui.cpp
index 7d256fd44f0..02818223094 100644
--- a/engines/director/lingo/xtras/m/mui.cpp
+++ b/engines/director/lingo/xtras/m/mui.cpp
@@ -31,6 +31,8 @@
  *
  * USED IN:
  * I Spy Spooky Mansion
+ * TKKG 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist Discovers the Sea
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/r/rtk.cpp b/engines/director/lingo/xtras/r/rtk.cpp
index ffbb1d54317..27b289679cb 100644
--- a/engines/director/lingo/xtras/r/rtk.cpp
+++ b/engines/director/lingo/xtras/r/rtk.cpp
@@ -34,6 +34,7 @@
  *
  * USED IN:
  * Cracking the Conspiracy
+ * Loewenzahn 1 (D5)
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/s/setmouse.cpp b/engines/director/lingo/xtras/s/setmouse.cpp
index 987e5775514..29d7e6d3742 100644
--- a/engines/director/lingo/xtras/s/setmouse.cpp
+++ b/engines/director/lingo/xtras/s/setmouse.cpp
@@ -30,7 +30,7 @@
 /**************************************************
  *
  * USED IN:
- * Loewenzahn 2 / 3
+ * Loewenzahn 2 / 3 / 4
  *
  **************************************************/
 




More information about the Scummvm-git-logs mailing list