[Scummvm-git-logs] scummvm master -> 08c9131b1abf5d5d95c24917e7dc3aef17e5dac8

lephilousophe noreply at scummvm.org
Tue Oct 15 20:45:54 UTC 2024


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

Summary:
e61dac6c68 BACKENDS: Allow write streams to be created atomically
a5f8df2d02 BACKENDS: Make sure log file is not written anymore when closing it
fdd11d0714 BACKENDS: FS: Implement atomic file write for stdio stream backend
e61ad40d2f BACKENDS: WIN32: Use Win32 specific API to move file atomically
08c9131b1a BACKENDS: FS: Fix Dreamcast build


Commit: e61dac6c683febf32585035786b9297ce02f2177
    https://github.com/scummvm/scummvm/commit/e61dac6c683febf32585035786b9297ce02f2177
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-10-15T22:45:50+02:00

Commit Message:
BACKENDS: Allow write streams to be created atomically

When creating an atomic write stream, the file is only created once it
is closed.
This can be done using a temporary file.
This commit only adds the API but not the proper implementation.

Changed paths:
    backends/fs/abstract-fs.h
    backends/fs/amigaos/amigaos-fs.cpp
    backends/fs/amigaos/amigaos-fs.h
    backends/fs/android/android-saf-fs.cpp
    backends/fs/android/android-saf-fs.h
    backends/fs/chroot/chroot-fs.cpp
    backends/fs/chroot/chroot-fs.h
    backends/fs/kolibrios/kolibrios-fs.cpp
    backends/fs/kolibrios/kolibrios-fs.h
    backends/fs/morphos/morphos-fs.cpp
    backends/fs/morphos/morphos-fs.h
    backends/fs/n64/n64-fs.cpp
    backends/fs/n64/n64-fs.h
    backends/fs/posix-drives/posix-drives-fs.cpp
    backends/fs/posix-drives/posix-drives-fs.h
    backends/fs/posix/posix-fs.cpp
    backends/fs/posix/posix-fs.h
    backends/fs/psp/psp-fs.cpp
    backends/fs/psp/psp-fs.h
    backends/fs/riscos/riscos-fs.cpp
    backends/fs/riscos/riscos-fs.h
    backends/fs/wii/wii-fs.cpp
    backends/fs/wii/wii-fs.h
    backends/fs/windows/windows-fs.cpp
    backends/fs/windows/windows-fs.h
    backends/platform/3ds/osystem.cpp
    backends/platform/dc/dc-fs.cpp
    backends/platform/libretro/include/libretro-fs.h
    backends/platform/libretro/src/libretro-fs.cpp
    backends/platform/sdl/sdl.cpp
    common/fs.cpp
    common/fs.h
    engines/director/lingo/lingo-the.cpp
    engines/testbed/config-params.cpp


diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h
index 54831079e89..14233750ad4 100644
--- a/backends/fs/abstract-fs.h
+++ b/backends/fs/abstract-fs.h
@@ -197,9 +197,14 @@ public:
 	 * referred by this node. This assumes that the node actually refers
 	 * to a readable file. If this is not the case, 0 is returned.
 	 *
+	 * When an atomic write stream is requested, the backend will write
+	 * the data in a temporary file before moving it to its final destination.
+	 *
+	 * @param atomic Request for an atomic file write when closing.
+	 *
 	 * @return pointer to the stream object, 0 in case of a failure
 	 */
-	virtual Common::SeekableWriteStream *createWriteStream() = 0;
+	virtual Common::SeekableWriteStream *createWriteStream(bool atomic) = 0;
 
 	/**
 	* Creates a directory referred by this node.
diff --git a/backends/fs/amigaos/amigaos-fs.cpp b/backends/fs/amigaos/amigaos-fs.cpp
index dfe87c008b9..b22e56330d4 100644
--- a/backends/fs/amigaos/amigaos-fs.cpp
+++ b/backends/fs/amigaos/amigaos-fs.cpp
@@ -350,7 +350,7 @@ Common::SeekableReadStream *AmigaOSFilesystemNode::createReadStream() {
 }
 
 
-Common::SeekableWriteStream *AmigaOSFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *AmigaOSFilesystemNode::createWriteStream(bool atomic) {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/amigaos/amigaos-fs.h b/backends/fs/amigaos/amigaos-fs.h
index f811a8f5ed4..fa293ee08aa 100644
--- a/backends/fs/amigaos/amigaos-fs.h
+++ b/backends/fs/amigaos/amigaos-fs.h
@@ -114,7 +114,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 };
 
diff --git a/backends/fs/android/android-saf-fs.cpp b/backends/fs/android/android-saf-fs.cpp
index a3963fefed8..7554936be0b 100644
--- a/backends/fs/android/android-saf-fs.cpp
+++ b/backends/fs/android/android-saf-fs.cpp
@@ -450,7 +450,7 @@ Common::SeekableReadStream *AndroidSAFFilesystemNode::createReadStream() {
 	return new PosixIoStream(f);
 }
 
-Common::SeekableWriteStream *AndroidSAFFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *AndroidSAFFilesystemNode::createWriteStream(bool atomic) {
 	assert(_safTree != nullptr);
 
 	JNIEnv *env = JNI::getEnv();
@@ -459,6 +459,7 @@ Common::SeekableWriteStream *AndroidSAFFilesystemNode::createWriteStream() {
 		assert(_safParent);
 		jstring name = env->NewStringUTF(_newName.c_str());
 
+		// TODO: Add atomic support if possible
 		jobject child = env->CallObjectMethod(_safTree, _MID_createFile, _safParent, name);
 
 		env->DeleteLocalRef(name);
diff --git a/backends/fs/android/android-saf-fs.h b/backends/fs/android/android-saf-fs.h
index 8cb33f45e9c..624b69594dd 100644
--- a/backends/fs/android/android-saf-fs.h
+++ b/backends/fs/android/android-saf-fs.h
@@ -132,7 +132,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 
 	bool remove() override;
@@ -182,7 +182,7 @@ public:
 	bool isWritable() const override;
 
 	Common::SeekableReadStream *createReadStream() override { return nullptr; }
-	Common::SeekableWriteStream *createWriteStream() override { return nullptr; }
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override { return nullptr; }
 
 	bool createDirectory() override { return false; }
 	bool remove() override { return false; }
diff --git a/backends/fs/chroot/chroot-fs.cpp b/backends/fs/chroot/chroot-fs.cpp
index 5629aebdb74..d7897cb17a8 100644
--- a/backends/fs/chroot/chroot-fs.cpp
+++ b/backends/fs/chroot/chroot-fs.cpp
@@ -100,8 +100,8 @@ Common::SeekableReadStream *ChRootFilesystemNode::createReadStream() {
 	return _realNode->createReadStream();
 }
 
-Common::SeekableWriteStream *ChRootFilesystemNode::createWriteStream() {
-	return _realNode->createWriteStream();
+Common::SeekableWriteStream *ChRootFilesystemNode::createWriteStream(bool atomic) {
+	return _realNode->createWriteStream(atomic);
 }
 
 bool ChRootFilesystemNode::createDirectory() {
diff --git a/backends/fs/chroot/chroot-fs.h b/backends/fs/chroot/chroot-fs.h
index 8d80a95769f..ff58bf51cef 100644
--- a/backends/fs/chroot/chroot-fs.h
+++ b/backends/fs/chroot/chroot-fs.h
@@ -48,7 +48,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 
 private:
diff --git a/backends/fs/kolibrios/kolibrios-fs.cpp b/backends/fs/kolibrios/kolibrios-fs.cpp
index 0a2456bdecd..6125c59bdd8 100644
--- a/backends/fs/kolibrios/kolibrios-fs.cpp
+++ b/backends/fs/kolibrios/kolibrios-fs.cpp
@@ -218,7 +218,7 @@ Common::SeekableReadStream *KolibriOSFilesystemNode::createReadStream() {
 	return PosixIoStream::makeFromPath(getPath(), false);
 }
 
-Common::SeekableWriteStream *KolibriOSFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *KolibriOSFilesystemNode::createWriteStream(bool atomic) {
 	return PosixIoStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/kolibrios/kolibrios-fs.h b/backends/fs/kolibrios/kolibrios-fs.h
index ffa6fd75f45..cebd6b9a517 100644
--- a/backends/fs/kolibrios/kolibrios-fs.h
+++ b/backends/fs/kolibrios/kolibrios-fs.h
@@ -52,7 +52,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 
 protected:
diff --git a/backends/fs/morphos/morphos-fs.cpp b/backends/fs/morphos/morphos-fs.cpp
index 4193bc34205..f39606d0653 100644
--- a/backends/fs/morphos/morphos-fs.cpp
+++ b/backends/fs/morphos/morphos-fs.cpp
@@ -352,7 +352,7 @@ Common::SeekableReadStream *MorphOSFilesystemNode::createReadStream() {
 	return readStream;
 }
 
-Common::SeekableWriteStream *MorphOSFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *MorphOSFilesystemNode::createWriteStream(bool atomic) {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/morphos/morphos-fs.h b/backends/fs/morphos/morphos-fs.h
index a515bc6876b..7e2842efefc 100644
--- a/backends/fs/morphos/morphos-fs.h
+++ b/backends/fs/morphos/morphos-fs.h
@@ -115,7 +115,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 };
 
diff --git a/backends/fs/n64/n64-fs.cpp b/backends/fs/n64/n64-fs.cpp
index 6ab6a81372c..25df9460817 100644
--- a/backends/fs/n64/n64-fs.cpp
+++ b/backends/fs/n64/n64-fs.cpp
@@ -155,7 +155,8 @@ Common::SeekableReadStream *N64FilesystemNode::createReadStream() {
 	return RomfsStream::makeFromPath(getPath(), false);
 }
 
-Common::SeekableWriteStream *N64FilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *N64FilesystemNode::createWriteStream(bool atomic) {
+	// TODO: Add atomic support if possible
 	return RomfsStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/n64/n64-fs.h b/backends/fs/n64/n64-fs.h
index 26ba6af3460..866f7f835af 100644
--- a/backends/fs/n64/n64-fs.h
+++ b/backends/fs/n64/n64-fs.h
@@ -71,7 +71,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 };
 
diff --git a/backends/fs/posix-drives/posix-drives-fs.cpp b/backends/fs/posix-drives/posix-drives-fs.cpp
index df3b8fcbafe..af078a6374e 100644
--- a/backends/fs/posix-drives/posix-drives-fs.cpp
+++ b/backends/fs/posix-drives/posix-drives-fs.cpp
@@ -81,7 +81,7 @@ Common::SeekableReadStream *DrivePOSIXFilesystemNode::createReadStream() {
 	return readStream;
 }
 
-Common::SeekableWriteStream *DrivePOSIXFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *DrivePOSIXFilesystemNode::createWriteStream(bool atomic) {
 	StdioStream *writeStream = PosixIoStream::makeFromPath(getPath(), true);
 
 	configureStream(writeStream);
diff --git a/backends/fs/posix-drives/posix-drives-fs.h b/backends/fs/posix-drives/posix-drives-fs.h
index a94fca89cbb..48089b8fa16 100644
--- a/backends/fs/posix-drives/posix-drives-fs.h
+++ b/backends/fs/posix-drives/posix-drives-fs.h
@@ -66,7 +66,7 @@ public:
 
 	// AbstractFSNode API
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	AbstractFSNode *getChild(const Common::String &n) const override;
 	bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
 	AbstractFSNode *getParent() const override;
diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index d63af64a5fd..92920b4a64d 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -280,7 +280,7 @@ Common::SeekableReadStream *POSIXFilesystemNode::createReadStreamForAltStream(Co
 	return nullptr;
 }
 
-Common::SeekableWriteStream *POSIXFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *POSIXFilesystemNode::createWriteStream(bool atomic) {
 	return PosixIoStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/posix/posix-fs.h b/backends/fs/posix/posix-fs.h
index f1ed9607e84..1c5df04be61 100644
--- a/backends/fs/posix/posix-fs.h
+++ b/backends/fs/posix/posix-fs.h
@@ -67,7 +67,7 @@ public:
 
 	Common::SeekableReadStream *createReadStream() override;
 	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 
 protected:
diff --git a/backends/fs/psp/psp-fs.cpp b/backends/fs/psp/psp-fs.cpp
index f8719a542d3..1a3647a073f 100644
--- a/backends/fs/psp/psp-fs.cpp
+++ b/backends/fs/psp/psp-fs.cpp
@@ -230,9 +230,10 @@ Common::SeekableReadStream *PSPFilesystemNode::createReadStream() {
 	return Common::wrapBufferedSeekableReadStream(stream, READ_BUFFER_SIZE, DisposeAfterUse::YES);
 }
 
-Common::SeekableWriteStream *PSPFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *PSPFilesystemNode::createWriteStream(bool atomic) {
 	const uint32 WRITE_BUFFER_SIZE = 1024;
 
+	// TODO: Add atomic support if possible
 	Common::SeekableWriteStream *stream = PspIoStream::makeFromPath(getPath(), true);
 
 	return Common::wrapBufferedWriteStream(stream, WRITE_BUFFER_SIZE);
diff --git a/backends/fs/psp/psp-fs.h b/backends/fs/psp/psp-fs.h
index 8b3f9194761..2458dc7ff5c 100644
--- a/backends/fs/psp/psp-fs.h
+++ b/backends/fs/psp/psp-fs.h
@@ -63,7 +63,7 @@ public:
 	virtual AbstractFSNode *getParent() const;
 
 	virtual Common::SeekableReadStream *createReadStream();
-	virtual Common::SeekableWriteStream *createWriteStream();
+	virtual Common::SeekableWriteStream *createWriteStream(bool atomic);
 	virtual bool createDirectory();
 };
 
diff --git a/backends/fs/riscos/riscos-fs.cpp b/backends/fs/riscos/riscos-fs.cpp
index 9f130542dec..25aa7f166fa 100644
--- a/backends/fs/riscos/riscos-fs.cpp
+++ b/backends/fs/riscos/riscos-fs.cpp
@@ -209,7 +209,7 @@ Common::SeekableReadStream *RISCOSFilesystemNode::createReadStream() {
 	return StdioStream::makeFromPath(getPath(), false);
 }
 
-Common::SeekableWriteStream *RISCOSFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *RISCOSFilesystemNode::createWriteStream(bool atomic) {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/riscos/riscos-fs.h b/backends/fs/riscos/riscos-fs.h
index 4e07b4136fa..6cca9f59d44 100644
--- a/backends/fs/riscos/riscos-fs.h
+++ b/backends/fs/riscos/riscos-fs.h
@@ -66,7 +66,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 private:
 	/**
diff --git a/backends/fs/wii/wii-fs.cpp b/backends/fs/wii/wii-fs.cpp
index d6f62ee3f21..00d81f34f0a 100644
--- a/backends/fs/wii/wii-fs.cpp
+++ b/backends/fs/wii/wii-fs.cpp
@@ -208,7 +208,7 @@ Common::SeekableReadStream *WiiFilesystemNode::createReadStream() {
 	return readStream;
 }
 
-Common::SeekableWriteStream *WiiFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *WiiFilesystemNode::createWriteStream(bool atomic) {
 	StdioStream *writeStream = StdioStream::makeFromPath(getPath(), true);
 
 	// disable newlib's buffering, the device libraries handle caching
diff --git a/backends/fs/wii/wii-fs.h b/backends/fs/wii/wii-fs.h
index ab7c742a19f..1bd9bd2d0cc 100644
--- a/backends/fs/wii/wii-fs.h
+++ b/backends/fs/wii/wii-fs.h
@@ -67,7 +67,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 };
 
diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp
index 565ebc9bf7f..3e2e4481069 100644
--- a/backends/fs/windows/windows-fs.cpp
+++ b/backends/fs/windows/windows-fs.cpp
@@ -226,7 +226,7 @@ Common::SeekableReadStream *WindowsFilesystemNode::createReadStream() {
 	return StdioStream::makeFromPath(getPath(), false);
 }
 
-Common::SeekableWriteStream *WindowsFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *WindowsFilesystemNode::createWriteStream(bool atomic) {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/fs/windows/windows-fs.h b/backends/fs/windows/windows-fs.h
index 912b60376bb..d435c887bd0 100644
--- a/backends/fs/windows/windows-fs.h
+++ b/backends/fs/windows/windows-fs.h
@@ -78,7 +78,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
 	bool createDirectory() override;
 
 private:
diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp
index 1f6d42be820..3996c490a69 100644
--- a/backends/platform/3ds/osystem.cpp
+++ b/backends/platform/3ds/osystem.cpp
@@ -269,7 +269,7 @@ Common::WriteStream *OSystem_3DS::createLogFile() {
 		return nullptr;
 
 	Common::FSNode file(logFile);
-	Common::WriteStream *stream = file.createWriteStream();
+	Common::WriteStream *stream = file.createWriteStream(false);
 	if (stream)
 		_logFilePath = logFile;
 	return stream;
diff --git a/backends/platform/dc/dc-fs.cpp b/backends/platform/dc/dc-fs.cpp
index 1aa7cad3f5d..714c941c9fb 100644
--- a/backends/platform/dc/dc-fs.cpp
+++ b/backends/platform/dc/dc-fs.cpp
@@ -56,7 +56,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
-	Common::SeekableWriteStream *createWriteStream() override { return 0; }
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override { return 0; }
 	bool createDirectory() override { return false; }
 
 	static AbstractFSNode *makeFileNodePath(const Common::String &path);
diff --git a/backends/platform/libretro/include/libretro-fs.h b/backends/platform/libretro/include/libretro-fs.h
index c73416acbcf..07ce51eea68 100644
--- a/backends/platform/libretro/include/libretro-fs.h
+++ b/backends/platform/libretro/include/libretro-fs.h
@@ -85,7 +85,7 @@ public:
 	virtual AbstractFSNode *getParent() const;
 
 	virtual Common::SeekableReadStream *createReadStream();
-	virtual Common::SeekableWriteStream *createWriteStream();
+	virtual Common::SeekableWriteStream *createWriteStream(bool atomic);
 	virtual bool createDirectory();
 
 	static Common::String getHomeDir(void);
diff --git a/backends/platform/libretro/src/libretro-fs.cpp b/backends/platform/libretro/src/libretro-fs.cpp
index 14747f99954..e5aee111f58 100644
--- a/backends/platform/libretro/src/libretro-fs.cpp
+++ b/backends/platform/libretro/src/libretro-fs.cpp
@@ -161,7 +161,7 @@ Common::SeekableReadStream *LibRetroFilesystemNode::createReadStream() {
 	return StdioStream::makeFromPath(getPath(), false);
 }
 
-Common::SeekableWriteStream *LibRetroFilesystemNode::createWriteStream() {
+Common::SeekableWriteStream *LibRetroFilesystemNode::createWriteStream(bool atomic) {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index d1a2fe4731d..f694923ff9a 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -649,7 +649,7 @@ Common::WriteStream *OSystem_SDL::createLogFile() {
 		return nullptr;
 
 	Common::FSNode file(logFile);
-	Common::WriteStream *stream = file.createWriteStream();
+	Common::WriteStream *stream = file.createWriteStream(false);
 	if (stream)
 		_logFilePath = logFile;
 	return stream;
diff --git a/common/fs.cpp b/common/fs.cpp
index be0da790b71..5cf79e1c172 100644
--- a/common/fs.cpp
+++ b/common/fs.cpp
@@ -263,7 +263,7 @@ SeekableReadStream *FSNode::createReadStreamForAltStream(AltStreamType altStream
 	return _realNode->createReadStreamForAltStream(altStreamType);
 }
 
-SeekableWriteStream *FSNode::createWriteStream() const {
+SeekableWriteStream *FSNode::createWriteStream(bool atomic) const {
 	if (_realNode == nullptr)
 		return nullptr;
 
@@ -272,7 +272,7 @@ SeekableWriteStream *FSNode::createWriteStream() const {
 		return nullptr;
 	}
 
-	return _realNode->createWriteStream();
+	return _realNode->createWriteStream(atomic);
 }
 
 bool FSNode::createDirectory() const {
diff --git a/common/fs.h b/common/fs.h
index 0c999725430..b5c6f8d95a8 100644
--- a/common/fs.h
+++ b/common/fs.h
@@ -276,9 +276,14 @@ public:
 	 * referred by this node. This assumes that the node actually refers
 	 * to a readable file. If this is not the case, 0 is returned.
 	 *
+	 * When an atomic write stream is requested, the backend will write
+	 * the data in a temporary file before moving it to its final destination.
+	 *
+	 * @param atomic Request for an atomic file write when closing.
+	 *
 	 * @return Pointer to the stream object, 0 in case of a failure.
 	 */
-	SeekableWriteStream *createWriteStream() const;
+	SeekableWriteStream *createWriteStream(bool atomic = true) const;
 
 	/**
 	 * Create a directory referred by this node. This assumes that this
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 8e03adb6f97..04e985d5d94 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -1228,7 +1228,7 @@ void Lingo::setTheEntity(int entity, Datum &id, int field, Datum &d) {
 			Common::Path logPath = ConfMan.getPath("path").appendComponent(d.asString());
 			Common::FSNode out(logPath);
 			if (!out.exists())
-				out.createWriteStream();
+				out.createWriteStream(false);
 			if (out.isWritable())
 				g_director->_traceLogFile = logPath;
 			else
diff --git a/engines/testbed/config-params.cpp b/engines/testbed/config-params.cpp
index 5b60900fc20..f5de4817e23 100644
--- a/engines/testbed/config-params.cpp
+++ b/engines/testbed/config-params.cpp
@@ -46,7 +46,7 @@ void ConfigParams::initLogging(const Common::Path &dirname, const char *filename
 	setLogDirectory(dirname);
 	setLogFilename(filename);
 	if (enable) {
-		_ws = Common::FSNode(_logDirectory).getChild(_logFilename).createWriteStream();
+		_ws = Common::FSNode(_logDirectory).getChild(_logFilename).createWriteStream(false);
 	} else {
 		_ws = 0;
 	}


Commit: a5f8df2d0239edb84e28d5f8ad3834a853b30323
    https://github.com/scummvm/scummvm/commit/a5f8df2d0239edb84e28d5f8ad3834a853b30323
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-10-15T22:45:50+02:00

Commit Message:
BACKENDS: Make sure log file is not written anymore when closing it

Changed paths:
    backends/log/log.cpp


diff --git a/backends/log/log.cpp b/backends/log/log.cpp
index dc98dcb2f17..44271232b89 100644
--- a/backends/log/log.cpp
+++ b/backends/log/log.cpp
@@ -56,8 +56,11 @@ void Log::close() {
 		// Output a message to indicate that the log was closed successfully
 		print("--- Log closed successfully.\n");
 
-		delete _stream;
+		// This avoids a segfault if a warning is issued when deleting the stream
+		Common::WriteStream *stream = _stream;
 		_stream = nullptr;
+
+		delete stream;
 	}
 }
 


Commit: fdd11d0714e17ceae0b49f4e225b449ac84e7d3f
    https://github.com/scummvm/scummvm/commit/fdd11d0714e17ceae0b49f4e225b449ac84e7d3f
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-10-15T22:45:50+02:00

Commit Message:
BACKENDS: FS: Implement atomic file write for stdio stream backend

This implementation creates a temporary file (suffixed with .tmp) and
renames it to the expected file name when the file is closed.
If the renaming fails, the destination file is removed and renaming is
tried again to handle cases where renaming over an existing file is not
supported by the underlying OS.

Changed paths:
    backends/audiocd/macosx/macosx-audiocd.cpp
    backends/fs/amigaos/amigaos-fs.cpp
    backends/fs/kolibrios/kolibrios-fs.cpp
    backends/fs/morphos/morphos-fs.cpp
    backends/fs/posix-drives/posix-drives-fs.cpp
    backends/fs/posix/posix-fs.cpp
    backends/fs/posix/posix-iostream.h
    backends/fs/riscos/riscos-fs.cpp
    backends/fs/stdiostream.cpp
    backends/fs/stdiostream.h
    backends/fs/wii/wii-fs.cpp
    backends/fs/windows/windows-fs.cpp
    backends/platform/dc/dc-fs.cpp
    backends/platform/libretro/src/libretro-fs.cpp


diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index c55c767058d..c23d9a7856a 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -223,7 +223,7 @@ bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int dur
 
 	// Now load the AIFF track from the name
 	Common::Path fileName = _trackMap[track];
-	Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.toString(Common::Path::kNativeSeparator).c_str(), false);
+	Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.toString(Common::Path::kNativeSeparator).c_str(), StdioStream::WriteMode_Read);
 
 	if (!stream) {
 		warning("Failed to open track '%s'", fileName.toString(Common::Path::kNativeSeparator).c_str());
diff --git a/backends/fs/amigaos/amigaos-fs.cpp b/backends/fs/amigaos/amigaos-fs.cpp
index b22e56330d4..1fb47f7c740 100644
--- a/backends/fs/amigaos/amigaos-fs.cpp
+++ b/backends/fs/amigaos/amigaos-fs.cpp
@@ -346,12 +346,13 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const {
 }
 
 Common::SeekableReadStream *AmigaOSFilesystemNode::createReadStream() {
-	return StdioStream::makeFromPath(getPath(), false);
+	return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 
 Common::SeekableWriteStream *AmigaOSFilesystemNode::createWriteStream(bool atomic) {
-	return StdioStream::makeFromPath(getPath(), true);
+	return StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool AmigaOSFilesystemNode::createDirectory() {
diff --git a/backends/fs/kolibrios/kolibrios-fs.cpp b/backends/fs/kolibrios/kolibrios-fs.cpp
index 6125c59bdd8..5c4ef0ac767 100644
--- a/backends/fs/kolibrios/kolibrios-fs.cpp
+++ b/backends/fs/kolibrios/kolibrios-fs.cpp
@@ -215,11 +215,12 @@ AbstractFSNode *KolibriOSFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *KolibriOSFilesystemNode::createReadStream() {
-	return PosixIoStream::makeFromPath(getPath(), false);
+	return PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 Common::SeekableWriteStream *KolibriOSFilesystemNode::createWriteStream(bool atomic) {
-	return PosixIoStream::makeFromPath(getPath(), true);
+	return PosixIoStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool KolibriOSFilesystemNode::createDirectory() {
diff --git a/backends/fs/morphos/morphos-fs.cpp b/backends/fs/morphos/morphos-fs.cpp
index f39606d0653..6c94bb704e6 100644
--- a/backends/fs/morphos/morphos-fs.cpp
+++ b/backends/fs/morphos/morphos-fs.cpp
@@ -343,7 +343,7 @@ AbstractFSList MorphOSFilesystemNode::listVolumes() const {
 }
 
 Common::SeekableReadStream *MorphOSFilesystemNode::createReadStream() {
-	StdioStream *readStream = StdioStream::makeFromPath(getPath(), false);
+	StdioStream *readStream = StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 
 	if (readStream) {
 		readStream->setBufferSize(8192);
@@ -353,7 +353,8 @@ Common::SeekableReadStream *MorphOSFilesystemNode::createReadStream() {
 }
 
 Common::SeekableWriteStream *MorphOSFilesystemNode::createWriteStream(bool atomic) {
-	return StdioStream::makeFromPath(getPath(), true);
+	return StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool MorphOSFilesystemNode::createDirectory() {
diff --git a/backends/fs/posix-drives/posix-drives-fs.cpp b/backends/fs/posix-drives/posix-drives-fs.cpp
index af078a6374e..3d8157b1f9e 100644
--- a/backends/fs/posix-drives/posix-drives-fs.cpp
+++ b/backends/fs/posix-drives/posix-drives-fs.cpp
@@ -69,7 +69,7 @@ void DrivePOSIXFilesystemNode::configureStream(StdioStream *stream) {
 }
 
 Common::SeekableReadStream *DrivePOSIXFilesystemNode::createReadStream() {
-	StdioStream *readStream = PosixIoStream::makeFromPath(getPath(), false);
+	StdioStream *readStream = PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 
 	configureStream(readStream);
 
@@ -82,7 +82,8 @@ Common::SeekableReadStream *DrivePOSIXFilesystemNode::createReadStream() {
 }
 
 Common::SeekableWriteStream *DrivePOSIXFilesystemNode::createWriteStream(bool atomic) {
-	StdioStream *writeStream = PosixIoStream::makeFromPath(getPath(), true);
+	StdioStream *writeStream = PosixIoStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 
 	configureStream(writeStream);
 
diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index 92920b4a64d..368c7c87ebf 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -266,14 +266,14 @@ AbstractFSNode *POSIXFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() {
-	return PosixIoStream::makeFromPath(getPath(), false);
+	return PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 Common::SeekableReadStream *POSIXFilesystemNode::createReadStreamForAltStream(Common::AltStreamType altStreamType) {
 #ifdef MACOSX
 	if (altStreamType == Common::AltStreamType::MacResourceFork) {
 		// Check the actual fork on a Mac computer
-		return PosixIoStream::makeFromPath(getPath() + "/..namedfork/rsrc", false);
+		return PosixIoStream::makeFromPath(getPath() + "/..namedfork/rsrc", StdioStream::WriteMode_Read);
 	}
 #endif
 
@@ -281,7 +281,8 @@ Common::SeekableReadStream *POSIXFilesystemNode::createReadStreamForAltStream(Co
 }
 
 Common::SeekableWriteStream *POSIXFilesystemNode::createWriteStream(bool atomic) {
-	return PosixIoStream::makeFromPath(getPath(), true);
+	return PosixIoStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool POSIXFilesystemNode::createDirectory() {
diff --git a/backends/fs/posix/posix-iostream.h b/backends/fs/posix/posix-iostream.h
index 1f16ae44454..269f355bdc9 100644
--- a/backends/fs/posix/posix-iostream.h
+++ b/backends/fs/posix/posix-iostream.h
@@ -29,7 +29,7 @@
  */
 class PosixIoStream final : public StdioStream {
 public:
-	static StdioStream *makeFromPath(const Common::String &path, bool writeMode) {
+	static StdioStream *makeFromPath(const Common::String &path, StdioStream::WriteMode writeMode) {
 		return StdioStream::makeFromPathHelper(path, writeMode, [](void *handle) -> StdioStream * {
 			return new PosixIoStream(handle);
 		});
diff --git a/backends/fs/riscos/riscos-fs.cpp b/backends/fs/riscos/riscos-fs.cpp
index 25aa7f166fa..154a57460c6 100644
--- a/backends/fs/riscos/riscos-fs.cpp
+++ b/backends/fs/riscos/riscos-fs.cpp
@@ -206,11 +206,12 @@ AbstractFSNode *RISCOSFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *RISCOSFilesystemNode::createReadStream() {
-	return StdioStream::makeFromPath(getPath(), false);
+	return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 Common::SeekableWriteStream *RISCOSFilesystemNode::createWriteStream(bool atomic) {
-	return StdioStream::makeFromPath(getPath(), true);
+	return StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool RISCOSFilesystemNode::createDirectory() {
diff --git a/backends/fs/stdiostream.cpp b/backends/fs/stdiostream.cpp
index f548d402e97..0e145dbb1f1 100644
--- a/backends/fs/stdiostream.cpp
+++ b/backends/fs/stdiostream.cpp
@@ -33,13 +33,37 @@
 
 // Include this after windows.h so we don't get a warning for redefining ARRAYSIZE
 #include "backends/fs/stdiostream.h"
+#include "common/textconsole.h"
 
-StdioStream::StdioStream(void *handle) : _handle(handle) {
+StdioStream::StdioStream(void *handle) : _handle(handle), _path(nullptr) {
 	assert(handle);
 }
 
 StdioStream::~StdioStream() {
 	fclose((FILE *)_handle);
+
+	if (!_path) {
+		return;
+	}
+
+	// _path is set: recreate the temporary file name and rename the file to
+	// its real name
+	Common::String tmpPath(*_path);
+	tmpPath += ".tmp";
+
+	if (!rename(tmpPath.c_str(), _path->c_str())) {
+		// Success
+		delete _path;
+		return;
+	}
+
+	// Error: try to delete the file first
+	(void)remove(_path->c_str());
+	if (rename(tmpPath.c_str(), _path->c_str())) {
+		warning("Couldn't save file %s", _path->c_str());
+	}
+
+	delete _path;
 }
 
 bool StdioStream::err() const {
@@ -124,21 +148,36 @@ bool StdioStream::flush() {
 	return fflush((FILE *)_handle) == 0;
 }
 
-StdioStream *StdioStream::makeFromPathHelper(const Common::String &path, bool writeMode,
+StdioStream *StdioStream::makeFromPathHelper(const Common::String &path, WriteMode writeMode,
 		StdioStream *(*factory)(void *handle)) {
+	Common::String tmpPath(path);
+	// In atomic mode we create a temporary file and rename it when closing the file descriptor
+	if (writeMode == WriteMode_WriteAtomic) {
+		tmpPath += ".tmp";
+	}
 #if defined(WIN32) && defined(UNICODE)
-	wchar_t *wPath = Win32::stringToTchar(path);
-	FILE *handle = _wfopen(wPath, writeMode ? L"wb" : L"rb");
+	wchar_t *wPath = Win32::stringToTchar(tmpPath);
+	FILE *handle = _wfopen(wPath, writeMode == WriteMode_Read ? L"rb" : L"wb");
 	free(wPath);
 #elif defined(HAS_FOPEN64)
-	FILE *handle = fopen64(path.c_str(), writeMode ? "wb" : "rb");
+	FILE *handle = fopen64(tmpPath.c_str(), writeMode == WriteMode_Read ? "rb" : "wb");
 #else
-	FILE *handle = fopen(path.c_str(), writeMode ? "wb" : "rb");
+	FILE *handle = fopen(tmpPath.c_str(), writeMode == WriteMode_Read ? "rb" : "wb");
 #endif
 
-	if (handle)
-		return factory(handle);
-	return nullptr;
+	if (!handle) {
+		return nullptr;
+	}
+
+	StdioStream *stream = factory(handle);
+	// Store the final path alongside the stream
+	// If _path is not nullptr, it will be used to rename the file
+	// when closing it
+	if (writeMode == WriteMode_WriteAtomic) {
+		stream->_path = new Common::String(path);
+	}
+
+	return stream;
 }
 
 #endif
diff --git a/backends/fs/stdiostream.h b/backends/fs/stdiostream.h
index c6856260b88..ffd256ab23f 100644
--- a/backends/fs/stdiostream.h
+++ b/backends/fs/stdiostream.h
@@ -28,11 +28,19 @@
 #include "common/str.h"
 
 class StdioStream : public Common::SeekableReadStream, public Common::SeekableWriteStream, public Common::NonCopyable {
+public:
+	enum WriteMode {
+		WriteMode_Read = 0,
+		WriteMode_Write = 1,
+		WriteMode_WriteAtomic = 2,
+	};
+
 protected:
 	/** File handle to the actual file. */
 	void *_handle;
+	Common::String *_path;
 
-	static StdioStream *makeFromPathHelper(const Common::String &path, bool writeMode,
+	static StdioStream *makeFromPathHelper(const Common::String &path, WriteMode writeMode,
 			StdioStream *(*factory)(void *handle));
 
 public:
@@ -40,7 +48,7 @@ public:
 	 * Given a path, invokes fopen on that path and wrap the result in a
 	 * StdioStream instance.
 	 */
-	static StdioStream *makeFromPath(const Common::String &path, bool writeMode) {
+	static StdioStream *makeFromPath(const Common::String &path, WriteMode writeMode) {
 		return makeFromPathHelper(path, writeMode, [](void *handle) {
 			return new StdioStream(handle);
 		});
diff --git a/backends/fs/wii/wii-fs.cpp b/backends/fs/wii/wii-fs.cpp
index 00d81f34f0a..27622e91e04 100644
--- a/backends/fs/wii/wii-fs.cpp
+++ b/backends/fs/wii/wii-fs.cpp
@@ -198,7 +198,7 @@ AbstractFSNode *WiiFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *WiiFilesystemNode::createReadStream() {
-	StdioStream *readStream = StdioStream::makeFromPath(getPath(), false);
+	StdioStream *readStream = StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 
 	// disable newlib's buffering, the device libraries handle caching
 	if (readStream) {
@@ -209,7 +209,8 @@ Common::SeekableReadStream *WiiFilesystemNode::createReadStream() {
 }
 
 Common::SeekableWriteStream *WiiFilesystemNode::createWriteStream(bool atomic) {
-	StdioStream *writeStream = StdioStream::makeFromPath(getPath(), true);
+	StdioStream *writeStream = StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 
 	// disable newlib's buffering, the device libraries handle caching
 	if (writeStream) {
diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp
index 3e2e4481069..45afa0f718e 100644
--- a/backends/fs/windows/windows-fs.cpp
+++ b/backends/fs/windows/windows-fs.cpp
@@ -223,11 +223,12 @@ AbstractFSNode *WindowsFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *WindowsFilesystemNode::createReadStream() {
-	return StdioStream::makeFromPath(getPath(), false);
+	return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 Common::SeekableWriteStream *WindowsFilesystemNode::createWriteStream(bool atomic) {
-	return StdioStream::makeFromPath(getPath(), true);
+	return StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool WindowsFilesystemNode::createDirectory() {
diff --git a/backends/platform/dc/dc-fs.cpp b/backends/platform/dc/dc-fs.cpp
index 714c941c9fb..4763d30314e 100644
--- a/backends/platform/dc/dc-fs.cpp
+++ b/backends/platform/dc/dc-fs.cpp
@@ -155,7 +155,7 @@ AbstractFSNode *RoninCDFileNode::getParent() const {
 
 
 Common::SeekableReadStream *RoninCDFileNode::createReadStream() {
-	return StdioStream::makeFromPath(getPath().c_str(), false);
+	return StdioStream::makeFromPath(getPath().c_str(), StdioStream::WriteMode_Read);
 }
 
 AbstractFSNode *OSystem_Dreamcast::makeRootFileNode() const {
diff --git a/backends/platform/libretro/src/libretro-fs.cpp b/backends/platform/libretro/src/libretro-fs.cpp
index e5aee111f58..150844a74a7 100644
--- a/backends/platform/libretro/src/libretro-fs.cpp
+++ b/backends/platform/libretro/src/libretro-fs.cpp
@@ -158,11 +158,12 @@ AbstractFSNode *LibRetroFilesystemNode::getParent() const {
 }
 
 Common::SeekableReadStream *LibRetroFilesystemNode::createReadStream() {
-	return StdioStream::makeFromPath(getPath(), false);
+	return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
 }
 
 Common::SeekableWriteStream *LibRetroFilesystemNode::createWriteStream(bool atomic) {
-	return StdioStream::makeFromPath(getPath(), true);
+	return StdioStream::makeFromPath(getPath(), atomic ?
+			StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
 }
 
 bool LibRetroFilesystemNode::createDirectory() {


Commit: e61ad40d2fa2a83fcb5d7137faefc4398955408c
    https://github.com/scummvm/scummvm/commit/e61ad40d2fa2a83fcb5d7137faefc4398955408c
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-10-15T22:45:50+02:00

Commit Message:
BACKENDS: WIN32: Use Win32 specific API to move file atomically

This avoids to remove the destination file before moving the temporary one.

Co-authored-by: Le Philousophe <lephilousophe at users.noreply.github.com>

Changed paths:
    backends/fs/stdiostream.cpp
    backends/fs/stdiostream.h
    backends/platform/sdl/win32/win32_wrapper.cpp
    backends/platform/sdl/win32/win32_wrapper.h


diff --git a/backends/fs/stdiostream.cpp b/backends/fs/stdiostream.cpp
index 0e145dbb1f1..6f973d71ecb 100644
--- a/backends/fs/stdiostream.cpp
+++ b/backends/fs/stdiostream.cpp
@@ -24,8 +24,8 @@
 // Disable symbol overrides so that we can use FILE, fopen etc.
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-// for Windows unicode fopen(): _wfopen()
-#if defined(WIN32) && defined(UNICODE)
+// for Windows unicode fopen(): _wfopen() and for Win32::moveFile()
+#if defined(WIN32)
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include "backends/platform/sdl/win32/win32_wrapper.h"
@@ -51,21 +51,29 @@ StdioStream::~StdioStream() {
 	Common::String tmpPath(*_path);
 	tmpPath += ".tmp";
 
-	if (!rename(tmpPath.c_str(), _path->c_str())) {
-		// Success
-		delete _path;
-		return;
-	}
-
-	// Error: try to delete the file first
-	(void)remove(_path->c_str());
-	if (rename(tmpPath.c_str(), _path->c_str())) {
+	if (!moveFile(tmpPath, *_path)) {
 		warning("Couldn't save file %s", _path->c_str());
 	}
 
 	delete _path;
 }
 
+bool StdioStream::moveFile(const Common::String &src, const Common::String &dst) {
+	// This piece of code can't be in a subclass override, as moveFile is called from the destructor.
+	// In this case, the vtable is reset to the StdioStream one before calling moveFile.
+#if defined(WIN32)
+	return Win32::moveFile(src, dst);
+#else
+	if (!rename(src.c_str(), dst.c_str())) {
+		return true;
+	}
+
+	// Error: try to delete the file first
+	(void)remove(dst.c_str());
+	return !rename(src.c_str(), dst.c_str());
+#endif
+}
+
 bool StdioStream::err() const {
 	return ferror((FILE *)_handle) != 0;
 }
diff --git a/backends/fs/stdiostream.h b/backends/fs/stdiostream.h
index ffd256ab23f..041b6d8382d 100644
--- a/backends/fs/stdiostream.h
+++ b/backends/fs/stdiostream.h
@@ -79,6 +79,20 @@ public:
 	 * @return success or failure
 	 */
 	bool setBufferSize(uint32 bufferSize);
+
+private:
+	/**
+	 * Move the file from src to dst.
+	 * This must succeed even if the destination file already exists.
+	 *
+	 * This function cannot be overridden as it's called from the destructor.
+	 *
+	 * @param src The file to move
+	 * @param dst The path where the file is to be moved.
+	 *
+	 * @returns Wether the renaming succeeded or not.
+	 */
+	bool moveFile(const Common::String &src, const Common::String &dst);
 };
 
 #endif
diff --git a/backends/platform/sdl/win32/win32_wrapper.cpp b/backends/platform/sdl/win32/win32_wrapper.cpp
index b38a5d06664..ebaca46e5a3 100644
--- a/backends/platform/sdl/win32/win32_wrapper.cpp
+++ b/backends/platform/sdl/win32/win32_wrapper.cpp
@@ -40,6 +40,7 @@
 #include <shlobj.h>
 #include <tchar.h>
 
+#include "common/ptr.h"
 #include "common/scummsys.h"
 #include "common/textconsole.h"
 #include "backends/platform/sdl/win32/win32_wrapper.h"
@@ -141,6 +142,33 @@ bool confirmWindowsVersion(int majorVersion, int minorVersion) {
 	return VerifyVersionInfoFunc(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask);
 }
 
+// for using ScopedPtr with malloc/free
+template <typename T>
+struct Freer {
+	inline void operator()(T *object) {
+		free(object);
+	}
+};
+
+bool moveFile(const Common::String &src, const Common::String &dst) {
+	Common::ScopedPtr<TCHAR, Freer<TCHAR>> tSrc(stringToTchar(src));
+	Common::ScopedPtr<TCHAR, Freer<TCHAR>> tDst(stringToTchar(dst));
+
+	if (MoveFileEx(tSrc.get(), tDst.get(), MOVEFILE_REPLACE_EXISTING)) {
+		return true;
+	}
+
+	// MoveFileEx may not be supported on the platform (Win9x)
+	if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
+		// Fall back to deleting the destination before using MoveFile.
+		// MoveFile requires that the destination not already exist.
+		DeleteFile(tDst.get());
+		return MoveFile(tSrc.get(), tDst.get());
+	}
+
+	return false;
+}
+
 bool isDriveCD(char driveLetter) {
 	TCHAR drivePath[] = TEXT("x:\\");
 	drivePath[0] = (TCHAR)driveLetter;
diff --git a/backends/platform/sdl/win32/win32_wrapper.h b/backends/platform/sdl/win32/win32_wrapper.h
index cedb0b9c731..11e23a23015 100644
--- a/backends/platform/sdl/win32/win32_wrapper.h
+++ b/backends/platform/sdl/win32/win32_wrapper.h
@@ -62,6 +62,11 @@ void getProcessDirectory(TCHAR *processDirectory, DWORD size);
  */
 bool confirmWindowsVersion(int majorVersion, int minorVersion);
 
+/**
+ * Moves a file within the same volume. Replaces any existing file.
+ */
+bool moveFile(const Common::String &src, const Common::String &dst);
+
 /**
  * Returns true if the drive letter is a CDROM
  *


Commit: 08c9131b1abf5d5d95c24917e7dc3aef17e5dac8
    https://github.com/scummvm/scummvm/commit/08c9131b1abf5d5d95c24917e7dc3aef17e5dac8
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-10-15T22:45:50+02:00

Commit Message:
BACKENDS: FS: Fix Dreamcast build

Changed paths:
    backends/fs/stdiostream.cpp
    backends/fs/stdiostream.h


diff --git a/backends/fs/stdiostream.cpp b/backends/fs/stdiostream.cpp
index 6f973d71ecb..799eee41302 100644
--- a/backends/fs/stdiostream.cpp
+++ b/backends/fs/stdiostream.cpp
@@ -59,6 +59,7 @@ StdioStream::~StdioStream() {
 }
 
 bool StdioStream::moveFile(const Common::String &src, const Common::String &dst) {
+#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
 	// This piece of code can't be in a subclass override, as moveFile is called from the destructor.
 	// In this case, the vtable is reset to the StdioStream one before calling moveFile.
 #if defined(WIN32)
@@ -72,6 +73,9 @@ bool StdioStream::moveFile(const Common::String &src, const Common::String &dst)
 	(void)remove(dst.c_str());
 	return !rename(src.c_str(), dst.c_str());
 #endif
+#else // STDIOSTREAM_NO_ATOMIC_SUPPORT
+	return false;
+#endif
 }
 
 bool StdioStream::err() const {
@@ -159,10 +163,14 @@ bool StdioStream::flush() {
 StdioStream *StdioStream::makeFromPathHelper(const Common::String &path, WriteMode writeMode,
 		StdioStream *(*factory)(void *handle)) {
 	Common::String tmpPath(path);
+
+#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
 	// In atomic mode we create a temporary file and rename it when closing the file descriptor
 	if (writeMode == WriteMode_WriteAtomic) {
 		tmpPath += ".tmp";
 	}
+#endif
+
 #if defined(WIN32) && defined(UNICODE)
 	wchar_t *wPath = Win32::stringToTchar(tmpPath);
 	FILE *handle = _wfopen(wPath, writeMode == WriteMode_Read ? L"rb" : L"wb");
@@ -178,12 +186,15 @@ StdioStream *StdioStream::makeFromPathHelper(const Common::String &path, WriteMo
 	}
 
 	StdioStream *stream = factory(handle);
+
+#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
 	// Store the final path alongside the stream
 	// If _path is not nullptr, it will be used to rename the file
 	// when closing it
 	if (writeMode == WriteMode_WriteAtomic) {
 		stream->_path = new Common::String(path);
 	}
+#endif
 
 	return stream;
 }
diff --git a/backends/fs/stdiostream.h b/backends/fs/stdiostream.h
index 041b6d8382d..812dfb4f157 100644
--- a/backends/fs/stdiostream.h
+++ b/backends/fs/stdiostream.h
@@ -27,12 +27,19 @@
 #include "common/stream.h"
 #include "common/str.h"
 
+#if defined(__DC__)
+// libronin doesn't support rename
+#define STDIOSTREAM_NO_ATOMIC_SUPPORT
+#endif
+
 class StdioStream : public Common::SeekableReadStream, public Common::SeekableWriteStream, public Common::NonCopyable {
 public:
 	enum WriteMode {
 		WriteMode_Read = 0,
 		WriteMode_Write = 1,
+#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
 		WriteMode_WriteAtomic = 2,
+#endif
 	};
 
 protected:




More information about the Scummvm-git-logs mailing list