foxygit / doom Log in
commit 9f14544070d0f7418d5025955526ea363d7f905d
Author:     Roman Fomin <rfomin@gmail.com>
AuthorDate: Wed Jun 8 22:19:42 2022 +0700
Commit:     GitHub <noreply@github.com>
CommitDate: Wed Jun 8 17:19:42 2022 +0200

    Add support for non-latin paths and filenames on Windows (#1463)

    * Add support for non-latin paths and filenames on Windows

    * add missed newline

    * fix memory leaks

    * remove redundant declarations

    * rewrite to use explicit wrappers

    * don't call M_fopen() in droplay.c

    * attempt to fix clang warning

    * make conversion failure to UTF 8 non-fatal

    * forgot newline

    * fix return value, add comment

    * set errno if conversion to UTF8 fails

    * update HACKING.md
---
 HACKING.md            |  12 ++++
 src/deh_io.c          |   2 +-
 src/doom/g_game.c     |  10 +--
 src/doom/m_menu.c     |   2 +-
 src/doom/statdump.c   |   3 +-
 src/gusconf.c         |   2 +-
 src/heretic/d_main.c  |   2 +-
 src/heretic/mn_menu.c |   2 +-
 src/heretic/p_saveg.c |   4 +-
 src/hexen/h2_main.c   |   2 +-
 src/hexen/mn_menu.c   |   2 +-
 src/hexen/sv_save.c   |  14 ++--
 src/i_glob.c          |   3 +-
 src/i_musicpack.c     |   4 +-
 src/i_oplmusic.c      |   2 +-
 src/i_sdlmusic.c      |   6 +-
 src/i_sdlsound.c      |   2 +-
 src/m_argv.c          |   2 +-
 src/m_config.c        |   4 +-
 src/m_misc.c          | 192 +++++++++++++++++++++++++++++++++++++++++++-------
 src/m_misc.h          |   6 +-
 src/midifile.c        |   3 +-
 src/net_common.c      |   3 +-
 src/setup/execute.c   |   8 +--
 src/strife/g_game.c   |   8 +--
 src/strife/m_menu.c   |   2 +-
 src/strife/m_saves.c  |  14 ++--
 src/v_video.c         |   2 +-
 src/w_file_stdc.c     |   2 +-
 src/w_wad.c           |   2 +-
 30 files changed, 242 insertions(+), 80 deletions(-)

diff --git a/HACKING.md b/HACKING.md
index c0f10d84..57818478 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -216,6 +216,18 @@ For signed integers, you shouldn’t assume that `(i >> n)` is the same as
 `(i / (1 << n))`.  However, most processors handle bitshifts of signed
 integers properly, so it’s not a huge problem.

+Due to Microsoft implementation of the Unicode support in C standard
+library you should use these wrappers to support non-latin paths and
+filenames on Windows.
+
+C library function |   Wrapper
+-------------------|-------------------
+`fopen()`          |  `M_fopen()`
+`remove()`         |  `M_remove()`
+`rename()`         |  `M_rename()`
+`stat()`           |  `M_stat()`
+`mkdir()`          |  `M_MakeDirectory()`
+
 ## GNU GPL and licensing

 All code submitted to the project must be licensed under the GNU GPLv2 or a
diff --git a/src/deh_io.c b/src/deh_io.c
index 78cffed3..b276a28c 100644
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -88,7 +88,7 @@ deh_context_t *DEH_OpenFile(const char *filename)
     FILE *fstream;
     deh_context_t *context;

-    fstream = fopen(filename, "r");
+    fstream = M_fopen(filename, "r");

     if (fstream == NULL)
         return NULL;
diff --git a/src/doom/g_game.c b/src/doom/g_game.c
index c5e2e496..d386b347 100644
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -1586,7 +1586,7 @@ void G_DoLoadGame (void)

     gameaction = ga_nothing;

-    save_stream = fopen(savename, "rb");
+    save_stream = M_fopen(savename, "rb");

     if (save_stream == NULL)
     {
@@ -1656,14 +1656,14 @@ void G_DoSaveGame (void)
     // and then rename it at the end if it was successfully written.
     // This prevents an existing savegame from being overwritten by
     // a corrupted one, or if a savegame buffer overrun occurs.
-    save_stream = fopen(temp_savegame_file, "wb");
+    save_stream = M_fopen(temp_savegame_file, "wb");

     if (save_stream == NULL)
     {
         // Failed to save the game, so we're going to have to abort. But
         // to be nice, save to somewhere else before we call I_Error().
         recovery_savegame_file = M_TempFile("recovery.dsg");
-        save_stream = fopen(recovery_savegame_file, "wb");
+        save_stream = M_fopen(recovery_savegame_file, "wb");
         if (save_stream == NULL)
         {
             I_Error("Failed to open either '%s' or '%s' to write savegame.",
@@ -1707,8 +1707,8 @@ void G_DoSaveGame (void)
     // Now rename the temporary savegame file to the actual savegame
     // file, overwriting the old savegame if there was one there.

-    remove(savegame_file);
-    rename(temp_savegame_file, savegame_file);
+    M_remove(savegame_file);
+    M_rename(temp_savegame_file, savegame_file);

     gameaction = ga_nothing;
     M_StringCopy(savedescription, "", sizeof(savedescription));
diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c
index 0da4869b..bf46b908 100644
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -510,7 +510,7 @@ void M_ReadSaveStrings(void)
         int retval;
         M_StringCopy(name, P_SaveGameFile(i), sizeof(name));

-	handle = fopen(name, "rb");
+	handle = M_fopen(name, "rb");
         if (handle == NULL)
         {
             M_StringCopy(savegamestrings[i], EMPTYSTRING, SAVESTRINGSIZE);
diff --git a/src/doom/statdump.c b/src/doom/statdump.c
index d096a61e..ae6814a3 100644
--- a/src/doom/statdump.c
+++ b/src/doom/statdump.c
@@ -26,6 +26,7 @@
 #include "d_player.h"
 #include "d_mode.h"
 #include "m_argv.h"
+#include "m_misc.h"

 #include "statdump.h"

@@ -335,7 +336,7 @@ void StatDump(void)

         if (strcmp(myargv[i + 1], "-") != 0)
         {
-            dumpfile = fopen(myargv[i + 1], "w");
+            dumpfile = M_fopen(myargv[i + 1], "w");
         }
         else
         {
diff --git a/src/gusconf.c b/src/gusconf.c
index 1906d8df..eed6c5b2 100644
--- a/src/gusconf.c
+++ b/src/gusconf.c
@@ -229,7 +229,7 @@ static boolean WriteTimidityConfig(char *path, gus_config_t *config)
     FILE *fstream;
     unsigned int i;

-    fstream = fopen(path, "w");
+    fstream = M_fopen(path, "w");

     if (fstream == NULL)
     {
diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c
index 5f4c6693..c56bceee 100644
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -238,7 +238,7 @@ void D_DoomLoop(void)
     {
         char filename[20];
         M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
-        debugfile = fopen(filename, "w");
+        debugfile = M_fopen(filename, "w");
     }
     I_GraphicsCheckCommandLine();
     I_SetGrabMouseCallback(D_GrabMouseCallback);
diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c
index 3c91fe39..24245cb1 100644
--- a/src/heretic/mn_menu.c
+++ b/src/heretic/mn_menu.c
@@ -644,7 +644,7 @@ void MN_LoadSlotText(void)
     {
         int retval;
         filename = SV_Filename(i);
-        fp = fopen(filename, "rb+");
+        fp = M_fopen(filename, "rb+");
 	free(filename);

         if (!fp)
diff --git a/src/heretic/p_saveg.c b/src/heretic/p_saveg.c
index cf06433f..36cfc617 100644
--- a/src/heretic/p_saveg.c
+++ b/src/heretic/p_saveg.c
@@ -60,12 +60,12 @@ char *SV_Filename(int slot)

 void SV_Open(char *fileName)
 {
-    SaveGameFP = fopen(fileName, "wb");
+    SaveGameFP = M_fopen(fileName, "wb");
 }

 void SV_OpenRead(char *filename)
 {
-    SaveGameFP = fopen(filename, "rb");
+    SaveGameFP = M_fopen(filename, "rb");

     if (SaveGameFP == NULL)
     {
diff --git a/src/hexen/h2_main.c b/src/hexen/h2_main.c
index 5dc9e9bb..a728f59a 100644
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -822,7 +822,7 @@ void H2_GameLoop(void)
     {
         char filename[20];
         M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
-        debugfile = fopen(filename, "w");
+        debugfile = M_fopen(filename, "w");
     }
     I_SetWindowTitle(gamedescription);
     I_GraphicsCheckCommandLine();
diff --git a/src/hexen/mn_menu.c b/src/hexen/mn_menu.c
index cd5e404f..fb9c50d1 100644
--- a/src/hexen/mn_menu.c
+++ b/src/hexen/mn_menu.c
@@ -680,7 +680,7 @@ static boolean ReadDescriptionForSlot(int slot, char *description)

     M_snprintf(name, sizeof(name), "%shex%d.hxs", SavePath, slot);

-    fp = fopen(name, "rb");
+    fp = M_fopen(name, "rb");

     if (fp == NULL)
     {
diff --git a/src/hexen/sv_save.c b/src/hexen/sv_save.c
index a5e7278a..a62ce17b 100644
--- a/src/hexen/sv_save.c
+++ b/src/hexen/sv_save.c
@@ -3199,10 +3199,10 @@ static void ClearSaveSlot(int slot)
     {
         M_snprintf(fileName, sizeof(fileName),
                    "%shex%d%02d.hxs", SavePath, slot, i);
-        remove(fileName);
+        M_remove(fileName);
     }
     M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, slot);
-    remove(fileName);
+    M_remove(fileName);
 }

 //==========================================================================
@@ -3262,7 +3262,7 @@ static void CopyFile(char *source_name, char *dest_name)
     FILE *read_handle, *write_handle;
     int buf_count, read_count, write_count;

-    read_handle = fopen(source_name, "rb");
+    read_handle = M_fopen(source_name, "rb");
     if (read_handle == NULL)
     {
         I_Error ("Couldn't read file %s", source_name);
@@ -3281,7 +3281,7 @@ static void CopyFile(char *source_name, char *dest_name)
         Z_Free(buffer);
     }

-    write_handle = fopen(dest_name, "wb");
+    write_handle = M_fopen(dest_name, "wb");
     if (write_handle == NULL)
     {
         I_Error ("Couldn't read file %s", dest_name);
@@ -3327,7 +3327,7 @@ static boolean ExistingFile(char *name)
 {
     FILE *fp;

-    if ((fp = fopen(name, "rb")) != NULL)
+    if ((fp = M_fopen(name, "rb")) != NULL)
     {
         fclose(fp);
         return true;
@@ -3346,7 +3346,7 @@ static boolean ExistingFile(char *name)

 static void SV_OpenRead(char *fileName)
 {
-    SavingFP = fopen(fileName, "rb");
+    SavingFP = M_fopen(fileName, "rb");

     // Should never happen, only if hex6.hxs cannot ever be created.
     if (SavingFP == NULL)
@@ -3357,7 +3357,7 @@ static void SV_OpenRead(char *fileName)

 static void SV_OpenWrite(char *fileName)
 {
-    SavingFP = fopen(fileName, "wb");
+    SavingFP = M_fopen(fileName, "wb");
 }

 //==========================================================================
diff --git a/src/i_glob.c b/src/i_glob.c
index f0790709..d057bcee 100644
--- a/src/i_glob.c
+++ b/src/i_glob.c
@@ -27,7 +27,6 @@
 #if defined(_MSC_VER)
 // For Visual C++, we need to include the win_opendir module.
 #include <win_opendir.h>
-#include <sys/stat.h>
 #define S_ISDIR(m)      (((m)& S_IFMT) == S_IFDIR)
 #elif defined(HAVE_DIRENT_H)
 #include <dirent.h>
@@ -60,7 +59,7 @@ static boolean IsDirectory(char *dir, struct dirent *de)
         int result;

         filename = M_StringJoin(dir, DIR_SEPARATOR_S, de->d_name, NULL);
-        result = stat(filename, &sb);
+        result = M_stat(filename, &sb);
         free(filename);

         if (result != 0)
diff --git a/src/i_musicpack.c b/src/i_musicpack.c
index b922af38..da1b88f3 100644
--- a/src/i_musicpack.c
+++ b/src/i_musicpack.c
@@ -580,7 +580,7 @@ static void ReadLoopPoints(const char *filename, file_metadata_t *metadata)
     metadata->start_time = 0;
     metadata->end_time = -1;

-    fs = fopen(filename, "rb");
+    fs = M_fopen(filename, "rb");

     if (fs == NULL)
     {
@@ -1017,7 +1017,7 @@ static void DumpSubstituteConfig(const char *filename)
     unsigned int lumpnum;
     size_t h;

-    fs = fopen(filename, "w");
+    fs = M_fopen(filename, "w");

     if (fs == NULL)
     {
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index 05b5c0fa..5f3ab2d4 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -1673,7 +1673,7 @@ static void *I_OPL_RegisterSong(void *data, int len)

     // remove file now

-    remove(filename);
+    M_remove(filename);
     free(filename);

     return result;
diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c
index b85e1a1c..064004d3 100644
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -78,7 +78,7 @@ static boolean WriteWrapperTimidityConfig(char *write_path)
         return false;
     }

-    fstream = fopen(write_path, "w");
+    fstream = M_fopen(write_path, "w");

     if (fstream == NULL)
     {
@@ -144,7 +144,7 @@ static void RemoveTimidityConfig(void)
 {
     if (temp_timidity_cfg != NULL)
     {
-        remove(temp_timidity_cfg);
+        M_remove(temp_timidity_cfg);
         free(temp_timidity_cfg);
     }
 }
@@ -514,7 +514,7 @@ static void *I_SDL_RegisterSong(void *data, int len)

         if (strlen(snd_musiccmd) == 0)
         {
-            remove(filename);
+            M_remove(filename);
         }
     }

diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index e9fc3d70..a28e06bb 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -552,7 +552,7 @@ static void WriteWAV(char *filename, byte *data,
     unsigned int i;
     unsigned short s;

-    wav = fopen(filename, "wb");
+    wav = M_fopen(filename, "wb");

     // Header

diff --git a/src/m_argv.c b/src/m_argv.c
index 8afd1412..193910f0 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -86,7 +86,7 @@ static void LoadResponseFile(int argv_index, const char *filename)
     int i, k;

     // Read the response file into memory
-    handle = fopen(filename, "rb");
+    handle = M_fopen(filename, "rb");

     if (handle == NULL)
     {
diff --git a/src/m_config.c b/src/m_config.c
index b5e90791..f3dc027f 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -1853,7 +1853,7 @@ static void SaveDefaultCollection(default_collection_t *collection)
     int i, v;
     FILE *f;

-    f = fopen (collection->filename, "w");
+    f = M_fopen(collection->filename, "w");
     if (!f)
 	return; // can't write the file, but don't complain

@@ -2049,7 +2049,7 @@ static void LoadDefaultCollection(default_collection_t *collection)
     char strparm[100];

     // read the file in, overriding any set defaults
-    f = fopen(collection->filename, "r");
+    f = M_fopen(collection->filename, "r");

     if (f == NULL)
     {
diff --git a/src/m_misc.c b/src/m_misc.c
index 67b03b18..fb75a772 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -32,7 +32,6 @@
 #include <direct.h>
 #endif
 #else
-#include <sys/stat.h>
 #include <sys/types.h>
 #endif

@@ -48,6 +47,159 @@
 #include "w_wad.h"
 #include "z_zone.h"

+#ifdef _WIN32
+static wchar_t* ConvertToUtf8(const char *str)
+{
+    wchar_t *wstr = NULL;
+    int wlen = 0;
+
+    wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+
+    if (!wlen)
+    {
+        errno = EINVAL;
+        printf("Warning: Failed to convert path to UTF8\n");
+        return NULL;
+    }
+
+    wstr = malloc(sizeof(wchar_t) * wlen);
+
+    if (!wstr)
+    {
+        I_Error("ConvertToUtf8: Failed to allocate new string");
+        return NULL;
+    }
+
+    if (MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen) == 0)
+    {
+        errno = EINVAL;
+        printf("Warning: Failed to convert path to UTF8\n");
+        free(wstr);
+        return NULL;
+    }
+
+    return wstr;
+}
+#endif
+
+FILE* M_fopen(const char *filename, const char *mode)
+{
+#ifdef _WIN32
+    FILE *file;
+    wchar_t *wname = NULL;
+    wchar_t *wmode = NULL;
+
+    wname = ConvertToUtf8(filename);
+
+    if (!wname)
+    {
+        return NULL;
+    }
+
+    wmode = ConvertToUtf8(mode);
+
+    if (!wmode)
+    {
+        free(wname);
+        return NULL;
+    }
+
+    file = _wfopen(wname, wmode);
+
+    free(wname);
+    free(wmode);
+
+    return file;
+#else
+    return fopen(filename, mode);
+#endif
+}
+
+int M_remove(const char *path)
+{
+#ifdef _WIN32
+    wchar_t *wpath = NULL;
+    int ret;
+
+    wpath = ConvertToUtf8(path);
+
+    if (!wpath)
+    {
+        return 0;
+    }
+
+    ret = _wremove(wpath);
+
+    free(wpath);
+
+    return ret;
+#else
+    return remove(path);
+#endif
+}
+
+int M_rename(const char *oldname, const char *newname)
+{
+#ifdef _WIN32
+    wchar_t *wold = NULL;
+    wchar_t *wnew = NULL;
+    int ret;
+
+    wold = ConvertToUtf8(oldname);
+
+    if (!wold)
+    {
+        return 0;
+    }
+
+    wnew = ConvertToUtf8(newname);
+
+    if (!wnew)
+    {
+        free(wold);
+        return 0;
+    }
+
+    ret = _wrename(wold, wnew);
+
+    free(wold);
+    free(wnew);
+
+    return ret;
+#else
+    return rename(oldname, newname);
+#endif
+}
+
+int M_stat(const char *path, struct stat *buf)
+{
+#ifdef _WIN32
+    wchar_t *wpath = NULL;
+    struct _stat wbuf;
+    int ret;
+
+    wpath = ConvertToUtf8(path);
+
+    if (!wpath)
+    {
+        return -1;
+    }
+
+    ret = _wstat(wpath, &wbuf);
+
+    // The _wstat() function expects a struct _stat* parameter that is
+    // incompatible with struct stat*. We copy only the required compatible
+    // field.
+    buf->st_mode = wbuf.st_mode;
+
+    free(wpath);
+
+    return ret;
+#else
+    return stat(path, buf);
+#endif
+}
+
 //
 // Create a directory
 //
@@ -55,7 +207,18 @@
 void M_MakeDirectory(const char *path)
 {
 #ifdef _WIN32
-    mkdir(path);
+    wchar_t *wdir;
+
+    wdir = ConvertToUtf8(path);
+
+    if (!wdir)
+    {
+        return;
+    }
+
+    _wmkdir(wdir);
+
+    free(wdir);
 #else
     mkdir(path, 0755);
 #endif
@@ -67,7 +230,7 @@ boolean M_FileExists(const char *filename)
 {
     FILE *fstream;

-    fstream = fopen(filename, "r");
+    fstream = M_fopen(filename, "r");

     if (fstream != NULL)
     {
@@ -183,7 +346,7 @@ boolean M_WriteFile(const char *name, const void *source, int length)
     FILE *handle;
     int	count;

-    handle = fopen(name, "wb");
+    handle = M_fopen(name, "wb");

     if (handle == NULL)
 	return false;
@@ -208,7 +371,7 @@ int M_ReadFile(const char *name, byte **buffer)
     int	count, length;
     byte *buf;

-    handle = fopen(name, "rb");
+    handle = M_fopen(name, "rb");
     if (handle == NULL)
 	I_Error ("Couldn't read file %s", name);

@@ -655,25 +818,6 @@ int M_snprintf(char *buf, size_t buf_len, const char *s, ...)
     return result;
 }

-#ifdef _WIN32
-
-char *M_OEMToUTF8(const char *oem)
-{
-    unsigned int len = strlen(oem) + 1;
-    wchar_t *tmp;
-    char *result;
-
-    tmp = malloc(len * sizeof(wchar_t));
-    MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len);
-    result = malloc(len * 4);
-    WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL);
-    free(tmp);
-
-    return result;
-}
-
-#endif
-
 //
 // M_NormalizeSlashes
 //
diff --git a/src/m_misc.h b/src/m_misc.h
index 35bf9b41..202db51a 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -22,9 +22,14 @@

 #include <stdio.h>
 #include <stdarg.h>
+#include <sys/stat.h>

 #include "doomtype.h"

+FILE* M_fopen(const char *filename, const char *mode);
+int M_remove(const char *path);
+int M_rename(const char *oldname, const char *newname);
+int M_stat(const char *path, struct stat *buf);
 boolean M_WriteFile(const char *name, const void *source, int length);
 int M_ReadFile(const char *name, byte **buffer);
 void M_MakeDirectory(const char *dir);
@@ -49,7 +54,6 @@ boolean M_StringStartsWith(const char *s, const char *prefix);
 boolean M_StringEndsWith(const char *s, const char *suffix);
 int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args);
 int M_snprintf(char *buf, size_t buf_len, const char *s, ...) PRINTF_ATTR(3, 4);
-char *M_OEMToUTF8(const char *ansi);
 void M_NormalizeSlashes(char *str);

 #endif
diff --git a/src/midifile.c b/src/midifile.c
index 3a383df9..cb93c6ee 100644
--- a/src/midifile.c
+++ b/src/midifile.c
@@ -23,6 +23,7 @@
 #include "doomtype.h"
 #include "i_swap.h"
 #include "i_system.h"
+#include "m_misc.h"
 #include "midifile.h"

 #define HEADER_CHUNK_ID "MThd"
@@ -598,7 +599,7 @@ midi_file_t *MIDI_LoadFile(char *filename)

     // Open file

-    stream = fopen(filename, "rb");
+    stream = M_fopen(filename, "rb");

     if (stream == NULL)
     {
diff --git a/src/net_common.c b/src/net_common.c
index 7b4eb344..99b32c25 100644
--- a/src/net_common.c
+++ b/src/net_common.c
@@ -24,6 +24,7 @@
 #include "i_system.h"
 #include "i_timer.h"
 #include "m_argv.h"
+#include "m_misc.h"

 #include "net_common.h"
 #include "net_io.h"
@@ -469,7 +470,7 @@ void NET_OpenLog(void)
     p = M_CheckParmWithArgs("-netlog", 1);
     if (p > 0)
     {
-        net_debug = fopen(myargv[p + 1], "w");
+        net_debug = M_fopen(myargv[p + 1], "w");
         if (net_debug == NULL)
         {
             I_Error("Failed to open %s to write debug log.", myargv[p + 1]);
diff --git a/src/setup/execute.c b/src/setup/execute.c
index 9ef98a32..e297f40e 100644
--- a/src/setup/execute.c
+++ b/src/setup/execute.c
@@ -119,7 +119,7 @@ execute_context_t *NewExecuteContext(void)
     result = malloc(sizeof(execute_context_t));

     result->response_file = TempFile("chocolat.rsp");
-    result->stream = fopen(result->response_file, "w");
+    result->stream = M_fopen(result->response_file, "w");

     if (result->stream == NULL)
     {
@@ -368,7 +368,7 @@ int ExecuteDoom(execute_context_t *context)
     free(response_file_arg);

     // Destroy context
-    remove(context->response_file);
+    M_remove(context->response_file);
     free(context->response_file);
     free(context);

@@ -406,8 +406,8 @@ static void TestCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))

     // Delete the temporary config files

-    remove(main_cfg);
-    remove(extra_cfg);
+    M_remove(main_cfg);
+    M_remove(extra_cfg);
     free(main_cfg);
     free(extra_cfg);
 }
diff --git a/src/strife/g_game.c b/src/strife/g_game.c
index 4fb861c2..49af47cf 100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -1682,7 +1682,7 @@ void G_DoLoadGame (boolean userload)

     gameaction = ga_nothing;

-    save_stream = fopen(loadpath, "rb");
+    save_stream = M_fopen(loadpath, "rb");

     // [STRIFE] If the file does not exist, G_DoLoadLevel is called.
     if (save_stream == NULL)
@@ -1828,7 +1828,7 @@ void G_DoSaveGame (char *path)
     // This prevents an existing savegame from being overwritten by
     // a corrupted one, or if a savegame buffer overrun occurs.

-    save_stream = fopen(temp_savegame_file, "wb");
+    save_stream = M_fopen(temp_savegame_file, "wb");

     if (save_stream == NULL)
     {
@@ -1862,8 +1862,8 @@ void G_DoSaveGame (char *path)
     // Now rename the temporary savegame file to the actual savegame
     // file, overwriting the old savegame if there was one there.

-    remove(savegame_file);
-    rename(temp_savegame_file, savegame_file);
+    M_remove(savegame_file);
+    M_rename(temp_savegame_file, savegame_file);

     // haleyjd: free the savegame_file path
     Z_Free(savegame_file);
diff --git a/src/strife/m_menu.c b/src/strife/m_menu.c
index 31d2ee07..443bc119 100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@ -552,7 +552,7 @@ void M_ReadSaveStrings(void)
             Z_Free(fname);
         fname = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(i, "\\name"));

-        handle = fopen(fname, "rb");
+        handle = M_fopen(fname, "rb");
         if(handle == NULL)
         {
             M_StringCopy(savegamestrings[i], DEH_String(EMPTYSTRING),
diff --git a/src/strife/m_saves.c b/src/strife/m_saves.c
index ab677465..561ec6a2 100644
--- a/src/strife/m_saves.c
+++ b/src/strife/m_saves.c
@@ -68,7 +68,7 @@ void ClearTmp(void)
         {
             break;
         }
-        remove(path);
+        M_remove(path);
     }

     I_EndGlob(glob);
@@ -99,7 +99,7 @@ void ClearSlot(void)
             break;
         }

-        remove(filepath);
+        M_remove(filepath);
     }

     I_EndGlob(glob);
@@ -207,8 +207,8 @@ void M_SaveMoveMapToHere(void)
     // haleyjd: use M_FileExists, not access
     if(M_FileExists(mapsave))
     {
-        remove(heresave);
-        rename(mapsave, heresave);
+        M_remove(heresave);
+        M_rename(mapsave, heresave);
     }

     Z_Free(mapsave);
@@ -234,8 +234,8 @@ void M_SaveMoveHereToMap(void)

     if(M_FileExists(heresave))
     {
-        remove(mapsave);
-        rename(heresave, mapsave);
+        M_remove(mapsave);
+        M_rename(heresave, mapsave);
     }

     Z_Free(mapsave);
@@ -273,7 +273,7 @@ void M_ReadMisObj(void)
     // haleyjd: use M_SafeFilePath, not sprintf
     srcpath = M_SafeFilePath(savepathtemp, "mis_obj");

-    if((f = fopen(srcpath, "rb")))
+    if((f = M_fopen(srcpath, "rb")))
     {
         int retval = fread(mission_objective, 1, OBJECTIVE_LEN, f);
         fclose(f);
diff --git a/src/v_video.c b/src/v_video.c
index 8c5b4055..060f7e16 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -752,7 +752,7 @@ void WritePNGfile(char *filename, pixel_t *data,
         h_factor = 1;
     }

-    handle = fopen(filename, "wb");
+    handle = M_fopen(filename, "wb");
     if (!handle)
     {
         return;
diff --git a/src/w_file_stdc.c b/src/w_file_stdc.c
index 59a394da..2bb32de4 100644
--- a/src/w_file_stdc.c
+++ b/src/w_file_stdc.c
@@ -35,7 +35,7 @@ static wad_file_t *W_StdC_OpenFile(const char *path)
     stdc_wad_file_t *result;
     FILE *fstream;

-    fstream = fopen(path, "rb");
+    fstream = M_fopen(path, "rb");

     if (fstream == NULL)
     {
diff --git a/src/w_wad.c b/src/w_wad.c
index ebf89677..1b091834 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -508,7 +508,7 @@ void W_Profile (void)
     }
     profilecount++;

-    f = fopen ("waddump.txt","w");
+    f = M_fopen ("waddump.txt","w");
     name[8] = 0;

     for (i=0 ; i<numlumps ; i++)