foxygit / doom Log in
commit 8eb3200286d523379295143ce3f44d77ce036d4b
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Tue Apr 1 20:43:45 2014 -0400
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Tue Apr 1 20:43:45 2014 -0400

    Replace all snprintf() calls with M_snprintf().

    The Windows API has an _snprintf function that is not the same as
    Unix's snprintf(): if the string is truncated then no trailing NUL
    character is appended. This makes the function unsafe. Define a
    replacement/wrapper called M_snprintf that works the same but always
    appends a trailing NUL, for safety on Windows and other OSes that
    behave like this.

    Do the same thing for vsnprintf(), and update HACKING to list
    snprintf/vsnprintf as forbidden functions. This fixes #375;
    thanks to Quasar for pointing out the different behavior of these
    functions.
---
 HACKING                    |  5 ++++-
 src/d_iwad.c               |  8 ++++----
 src/deh_str.c              |  3 ++-
 src/doom/am_map.c          |  5 +++--
 src/doom/d_main.c          |  6 +++---
 src/doom/g_game.c          | 10 +++++-----
 src/doom/hu_stuff.c        |  4 ++--
 src/doom/p_saveg.c         |  6 +++---
 src/doom/s_sound.c         |  3 ++-
 src/doom/st_stuff.c        |  9 +++++----
 src/doomtype.h             |  4 ----
 src/heretic/am_map.c       |  6 +++---
 src/heretic/d_main.c       |  4 ++--
 src/heretic/f_finale.c     |  2 +-
 src/heretic/i_sound.c      | 34 +++++++++++++++++-----------------
 src/heretic/p_saveg.c      |  4 ++--
 src/heretic/sb_bar.c       | 14 +++++++-------
 src/hexen/am_map.c         | 13 +++++++------
 src/hexen/h2_main.c        |  6 +++---
 src/hexen/in_lude.c        |  9 +++++----
 src/hexen/mn_menu.c        |  2 +-
 src/hexen/p_acs.c          | 10 +++++-----
 src/hexen/p_setup.c        |  2 +-
 src/hexen/p_spec.c         |  5 +++--
 src/hexen/sb_bar.c         | 44 ++++++++++++++++++++++----------------------
 src/hexen/sc_man.c         |  2 +-
 src/hexen/sv_save.c        | 34 +++++++++++++++++-----------------
 src/i_oplmusic.c           | 28 ++++++++++++++--------------
 src/i_pcsound.c            |  2 +-
 src/i_sdlsound.c           |  6 +++---
 src/i_system.c             |  4 ++--
 src/i_video.c              |  4 ++--
 src/m_controls.c           |  3 ++-
 src/m_misc.c               | 33 +++++++++++++++++++++++++++++++++
 src/m_misc.h               |  4 +++-
 src/net_gui.c              |  7 ++++---
 src/net_loop.c             |  5 +++--
 src/net_sdl.c              | 13 +++++++------
 src/net_server.c           |  4 ++--
 src/setup/txt_mouseinput.c |  2 +-
 src/strife/am_map.c        |  5 +++--
 src/strife/d_main.c        |  6 +++---
 src/strife/g_game.c        | 16 ++++++++--------
 src/strife/m_saves.c       | 10 +++++-----
 src/strife/p_inter.c       |  8 ++++----
 src/strife/p_saveg.c       |  7 ++++---
 src/strife/p_user.c        |  5 +++--
 src/strife/s_sound.c       |  2 +-
 src/strife/st_stuff.c      | 11 ++++++-----
 src/v_video.c              |  2 +-
 50 files changed, 245 insertions(+), 196 deletions(-)

diff --git a/HACKING b/HACKING
index 0680a037..ddf3d35c 100644
--- a/HACKING
+++ b/HACKING
@@ -122,7 +122,10 @@ avoided when writing code for Chocolate Doom. These are:
     Unsafe function       Safer alternative
     ---------------------------------------------
     gets()                fgets(.., stdin)
-    sprintf               snprintf()
+    sprintf               M_snprintf()
+    snprintf              M_snprintf()
+    vsprintf              M_vsnprintf()
+    vsnprintf             M_vsnprintf()
     strcpy()              M_StringCopy()
     strncpy()             M_StringCopy()
     strcat()              M_StringConcat()
diff --git a/src/d_iwad.c b/src/d_iwad.c
index 508abf9d..3794164c 100644
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -351,14 +351,14 @@ static void CheckSteamGUSPatches(void)

     len = strlen(install_path) + strlen(STEAM_BFG_GUS_PATCHES) + 20;
     patch_path = malloc(len);
-    snprintf(patch_path, len, "%s\\%s\\ACBASS.PAT",
-             install_path, STEAM_BFG_GUS_PATCHES);
+    M_snprintf(patch_path, len, "%s\\%s\\ACBASS.PAT",
+               install_path, STEAM_BFG_GUS_PATCHES);

     // Does acbass.pat exist? If so, then set gus_patch_path.
     if (M_FileExists(patch_path))
     {
-        snprintf(patch_path, len, "%s\\%s",
-                 install_path, STEAM_BFG_GUS_PATCHES);
+        M_snprintf(patch_path, len, "%s\\%s",
+                   install_path, STEAM_BFG_GUS_PATCHES);
         M_SetVariable("gus_patch_path", patch_path);
     }

diff --git a/src/deh_str.c b/src/deh_str.c
index 9bd429b6..5639638d 100644
--- a/src/deh_str.c
+++ b/src/deh_str.c
@@ -31,6 +31,7 @@

 #include "doomtype.h"
 #include "deh_str.h"
+#include "m_misc.h"

 #include "z_zone.h"

@@ -401,7 +402,7 @@ void DEH_snprintf(char *buffer, size_t len, char *fmt, ...)

     va_start(args, fmt);

-    vsnprintf(buffer, len, repl, args);
+    M_vsnprintf(buffer, len, repl, args);

     va_end(args);
 }
diff --git a/src/doom/am_map.c b/src/doom/am_map.c
index b7bfd3bd..d920823f 100644
--- a/src/doom/am_map.c
+++ b/src/doom/am_map.c
@@ -38,6 +38,7 @@

 #include "m_cheat.h"
 #include "m_controls.h"
+#include "m_misc.h"
 #include "i_system.h"

 // Needs access to LFB.
@@ -689,8 +690,8 @@ AM_Responder
         }
         else if (key == key_map_mark)
         {
-            snprintf(buffer, sizeof(buffer), "%s %d",
-                     DEH_String(AMSTR_MARKEDSPOT), markpointnum);
+            M_snprintf(buffer, sizeof(buffer), "%s %d",
+                       DEH_String(AMSTR_MARKEDSPOT), markpointnum);
             plr->message = buffer;
             AM_addMark();
         }
diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index 0cdb83d4..2a4fe236 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -384,7 +384,7 @@ void D_BindVariables(void)
     {
         char buf[12];

-        snprintf(buf, sizeof(buf), "chatmacro%i", i);
+        M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
         M_BindVariable(buf, &chat_macros[i]);
     }
 }
@@ -669,8 +669,8 @@ static char *GetGameName(char *gamename)
             gamename_size = strlen(deh_sub) + 10;
             gamename = Z_Malloc(gamename_size, PU_STATIC, 0);
             version = G_VanillaVersionCode();
-            snprintf(gamename, gamename_size, deh_sub,
-                     version / 100, version % 100);
+            M_snprintf(gamename, gamename_size, deh_sub,
+                       version / 100, version % 100);

             while (gamename[0] != '\0' && isspace(gamename[0]))
             {
diff --git a/src/doom/g_game.c b/src/doom/g_game.c
index 034bb3ef..8dca36d9 100644
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -915,8 +915,8 @@ void G_Ticker (void)
             {
                 static char turbomessage[80];
                 extern char *player_names[4];
-                snprintf(turbomessage, sizeof(turbomessage),
-                         "%s is turbo!", player_names[i]);
+                M_snprintf(turbomessage, sizeof(turbomessage),
+                           "%s is turbo!", player_names[i]);
                 players[consoleplayer].message = turbomessage;
                 turbodetected[i] = false;
             }
@@ -1949,7 +1949,7 @@ void G_RecordDemo (char *name)
     usergame = false;
     demoname_size = strlen(name) + 5;
     demoname = Z_Malloc(demoname_size, PU_STATIC, NULL);
-    snprintf(demoname, demoname_size, "%s.lmp", name);
+    M_snprintf(demoname, demoname_size, "%s.lmp", name);
     maxsize = 0x20000;

     //!
@@ -2076,8 +2076,8 @@ static char *DemoVersionDescription(int version)
     }
     else
     {
-        snprintf(resultbuf, sizeof(resultbuf),
-                 "%i.%i (unknown)", version / 100, version % 100);
+        M_snprintf(resultbuf, sizeof(resultbuf),
+                   "%i.%i (unknown)", version / 100, version % 100);
         return resultbuf;
     }
 }
diff --git a/src/doom/hu_stuff.c b/src/doom/hu_stuff.c
index 8ea3912e..dd72e63f 100644
--- a/src/doom/hu_stuff.c
+++ b/src/doom/hu_stuff.c
@@ -627,8 +627,8 @@ boolean HU_Responder(event_t *ev)
 		// static unsigned char buf[20]; // DEBUG
 		HU_queueChatChar(c);

-		// snprintf(buf, sizeof(buf), "KEY: %d => %d", ev->data1, c);
-		//      plr->message = buf;
+		// M_snprintf(buf, sizeof(buf), "KEY: %d => %d", ev->data1, c);
+		//        plr->message = buf;
 	    }
 	    if (c == KEY_ENTER)
 	    {
diff --git a/src/doom/p_saveg.c b/src/doom/p_saveg.c
index 5fa70fea..a2f954a3 100644
--- a/src/doom/p_saveg.c
+++ b/src/doom/p_saveg.c
@@ -79,7 +79,7 @@ char *P_SaveGameFile(int slot)
     }

     DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot);
-    snprintf(filename, filename_size, "%s%s", savegamedir, basename);
+    M_snprintf(filename, filename_size, "%s%s", savegamedir, basename);

     return filename;
 }
@@ -1363,7 +1363,7 @@ void P_WriteSaveGameHeader(char *description)
         saveg_write8(0);

     memset(name, 0, sizeof(name));
-    snprintf(name, sizeof(name), "version %i", G_VanillaVersionCode());
+    M_snprintf(name, sizeof(name), "version %i", G_VanillaVersionCode());

     for (i=0; i<VERSIONSIZE; ++i)
         saveg_write8(name[i]);
@@ -1400,7 +1400,7 @@ boolean P_ReadSaveGameHeader(void)
         read_vcheck[i] = saveg_read8();

     memset(vcheck, 0, sizeof(vcheck));
-    snprintf(vcheck, sizeof(vcheck), "version %i", G_VanillaVersionCode());
+    M_snprintf(vcheck, sizeof(vcheck), "version %i", G_VanillaVersionCode());
     if (strcmp(read_vcheck, vcheck) != 0)
 	return false;				// bad version

diff --git a/src/doom/s_sound.c b/src/doom/s_sound.c
index 4e81b016..dc0473a3 100644
--- a/src/doom/s_sound.c
+++ b/src/doom/s_sound.c
@@ -38,6 +38,7 @@
 #include "sounds.h"
 #include "s_sound.h"

+#include "m_misc.h"
 #include "m_random.h"
 #include "m_argv.h"

@@ -638,7 +639,7 @@ void S_ChangeMusic(int musicnum, int looping)
     // get lumpnum if neccessary
     if (!music->lumpnum)
     {
-        snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name));
+        M_snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name));
         music->lumpnum = W_GetNumForName(namebuf);
     }

diff --git a/src/doom/st_stuff.c b/src/doom/st_stuff.c
index b9d9aefd..d00eeba1 100644
--- a/src/doom/st_stuff.c
+++ b/src/doom/st_stuff.c
@@ -33,6 +33,7 @@
 #include "i_system.h"
 #include "i_video.h"
 #include "z_zone.h"
+#include "m_misc.h"
 #include "m_random.h"
 #include "w_wad.h"

@@ -598,10 +599,10 @@ ST_Responder (event_t* ev)
       else if (cht_CheckCheat(&cheat_mypos, ev->data2))
       {
         static char buf[ST_MSGWIDTH];
-        snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)",
-                 players[consoleplayer].mo->angle,
-                 players[consoleplayer].mo->x,
-                 players[consoleplayer].mo->y);
+        M_snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)",
+                   players[consoleplayer].mo->angle,
+                   players[consoleplayer].mo->x,
+                   players[consoleplayer].mo->y);
         plyr->message = buf;
       }
     }
diff --git a/src/doomtype.h b/src/doomtype.h
index 626d69a8..8b4467f1 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -35,10 +35,6 @@

 #ifdef _WIN32

-#define snprintf _snprintf
-#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
-#define vsnprintf _vsnprintf
-#endif
 #define strcasecmp stricmp
 #define strncasecmp strnicmp

diff --git a/src/heretic/am_map.c b/src/heretic/am_map.c
index fb2d0f72..df3932a1 100644
--- a/src/heretic/am_map.c
+++ b/src/heretic/am_map.c
@@ -411,7 +411,7 @@ void AM_loadPics(void)
     //char namebuf[9];
 /*  for (i=0;i<10;i++)
   {
-    snprintf(namebuf, sizeof(namebuf), "AMMNUM%d", i);
+    M_snprintf(namebuf, sizeof(namebuf), "AMMNUM%d", i);
     marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
   }*/
     maplump = W_CacheLumpName(DEH_String("AUTOPAGE"), PU_STATIC);
@@ -603,8 +603,8 @@ boolean AM_Responder(event_t * ev)
         }
         else if (key == key_map_mark)
         {
-            snprintf(buffer, sizeof(buffer), "%s %d",
-                     AMSTR_MARKEDSPOT, markpointnum);
+            M_snprintf(buffer, sizeof(buffer), "%s %d",
+                       AMSTR_MARKEDSPOT, markpointnum);
             plr->message = buffer;
             AM_addMark();
         }
diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c
index 81fa6c0d..89c88108 100644
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -247,7 +247,7 @@ void D_DoomLoop(void)
     if (M_CheckParm("-debugfile"))
     {
         char filename[20];
-        snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
+        M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
         debugfile = fopen(filename, "w");
     }
     I_GraphicsCheckCommandLine();
@@ -776,7 +776,7 @@ void D_BindVariables(void)
     {
         char buf[12];

-        snprintf(buf, sizeof(buf), "chatmacro%i", i);
+        M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
         M_BindVariable(buf, &chat_macros[i]);
     }
 }
diff --git a/src/heretic/f_finale.c b/src/heretic/f_finale.c
index 7978e45b..c7212add 100644
--- a/src/heretic/f_finale.c
+++ b/src/heretic/f_finale.c
@@ -405,7 +405,7 @@ void F_BunnyScroll(void)
         laststage = stage;
     }

-    snprintf(name, sizeof(name), "END%i", stage);
+    M_snprintf(name, sizeof(name), "END%i", stage);
     V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2,
                 W_CacheLumpName(name, PU_CACHE));
 }
diff --git a/src/heretic/i_sound.c b/src/heretic/i_sound.c
index 2dc66239..f415ce0a 100644
--- a/src/heretic/i_sound.c
+++ b/src/heretic/i_sound.c
@@ -211,8 +211,8 @@ int I_GetSfxLumpNum(sfxinfo_t * sound)
         return 0;
     if (sound->link)
         sound = sound->link;
-//  snprintf(namebuf, sizeof(namebuf), "d%c%s",
-//           snd_prefixen[snd_SfxDevice], sound->name);
+//  M_snprintf(namebuf, sizeof(namebuf), "d%c%s",
+//             snd_prefixen[snd_SfxDevice], sound->name);
     return W_GetNumForName(sound->name);

 }
@@ -320,15 +320,15 @@ void I_sndArbitrateCards(void)
     {
         if (debugmode)
         {
-            snprintf(tmp, sizeof(tmp), "cfg p=0x%x, i=%d, d=%d\n",
-                     snd_SBport, snd_SBirq, snd_SBdma);
+            M_snprintf(tmp, sizeof(tmp), "cfg p=0x%x, i=%d, d=%d\n",
+                       snd_SBport, snd_SBirq, snd_SBdma);
             tprintf(tmp, 0);
         }
         if (SB_Detect(&snd_SBport, &snd_SBirq, &snd_SBdma, 0))
         {
-            snprintf(tmp, sizeof(tmp),
-                     "SB isn't responding at p=0x%x, i=%d, d=%d\n",
-                     snd_SBport, snd_SBirq, snd_SBdma);
+            M_snprintf(tmp, sizeof(tmp),
+                       "SB isn't responding at p=0x%x, i=%d, d=%d\n",
+                       snd_SBport, snd_SBirq, snd_SBdma);
             tprintf(tmp, 0);
         }
         else
@@ -336,8 +336,8 @@ void I_sndArbitrateCards(void)

         if (debugmode)
         {
-            snprintf(tmp, sizeof(tmp), "SB_Detect returned p=0x%x,i=%d,d=%d\n",
-                     snd_SBport, snd_SBirq, snd_SBdma);
+            M_snprintf(tmp, sizeof(tmp), "SB_Detect returned p=0x%x,i=%d,d=%d\n",
+                       snd_SBport, snd_SBirq, snd_SBdma);
             tprintf(tmp, 0);
         }
     }
@@ -354,14 +354,14 @@ void I_sndArbitrateCards(void)
     {
         if (debugmode)
         {
-            snprintf(tmp, sizeof(tmp), "cfg p=0x%x\n", snd_Mport);
+            M_snprintf(tmp, sizeof(tmp), "cfg p=0x%x\n", snd_Mport);
             tprintf(tmp, 0);
         }

         if (MPU_Detect(&snd_Mport, &i))
         {
-            snprintf(tmp, sizeof(tmp),
-                     "The MPU-401 isn't reponding @ p=0x%x.\n", snd_Mport);
+            M_snprintf(tmp, sizeof(tmp),
+                       "The MPU-401 isn't reponding @ p=0x%x.\n", snd_Mport);
             tprintf(tmp, 0);
         }
         else
@@ -399,11 +399,11 @@ void I_StartupSound(void)

     if (debugmode)
     {
-        snprintf(tmp, sizeof(tmp), "  Music device #%d & dmxCode=%d",
-                 snd_MusicDevice, dmxCodes[snd_MusicDevice]);
+        M_snprintf(tmp, sizeof(tmp), "  Music device #%d & dmxCode=%d",
+                   snd_MusicDevice, dmxCodes[snd_MusicDevice]);
         tprintf(tmp, 0);
-        snprintf(tmp, sizeof(tmp),  "  Sfx device #%d & dmxCode=%d\n",
-                 snd_SfxDevice, dmxCodes[snd_SfxDevice]);
+        M_snprintf(tmp, sizeof(tmp),  "  Sfx device #%d & dmxCode=%d\n",
+                   snd_SfxDevice, dmxCodes[snd_SfxDevice]);
         tprintf(tmp, 0);
     }

@@ -414,7 +414,7 @@ void I_StartupSound(void)

     if (debugmode)
     {
-        snprintf(tmp, sizeof(tmp), "  DMX_Init() returned %d", rc);
+        M_snprintf(tmp, sizeof(tmp), "  DMX_Init() returned %d", rc);
         tprintf(tmp, 0);
     }

diff --git a/src/heretic/p_saveg.c b/src/heretic/p_saveg.c
index 27e6423c..7b4c5389 100644
--- a/src/heretic/p_saveg.c
+++ b/src/heretic/p_saveg.c
@@ -55,8 +55,8 @@ char *SV_Filename(int slot)

     filename_len = strlen(savegamedir) + strlen(SAVEGAMENAME) + 8;
     filename = malloc(filename_len);
-    snprintf(filename, filename_len,
-             "%s" SAVEGAMENAME "%d.hsg", savegamedir, slot);
+    M_snprintf(filename, filename_len,
+               "%s" SAVEGAMENAME "%d.hsg", savegamedir, slot);

     return filename;
 }
diff --git a/src/heretic/sb_bar.c b/src/heretic/sb_bar.c
index 0a2da956..6821fb1e 100644
--- a/src/heretic/sb_bar.c
+++ b/src/heretic/sb_bar.c
@@ -484,20 +484,20 @@ static void DrawSoundInfo(void)
             MN_DrTextA(DEH_String("------"), xPos[0], y);
             continue;
         }
-        snprintf(text, sizeof(text), "%s", c->name);
+        M_snprintf(text, sizeof(text), "%s", c->name);
         M_ForceUppercase(text);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->type);
+        M_snprintf(text, sizeof(text), "%d", c->mo->type);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS);
+        M_snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS);
+        M_snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->id);
+        M_snprintf(text, sizeof(text), "%d", c->id);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->priority);
+        M_snprintf(text, sizeof(text), "%d", c->priority);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->distance);
+        M_snprintf(text, sizeof(text), "%d", c->distance);
         MN_DrTextA(text, xPos[x++], y);
     }
     UpdateState |= I_FULLSCRN;
diff --git a/src/hexen/am_map.c b/src/hexen/am_map.c
index 21b6c45c..cbbb3586 100644
--- a/src/hexen/am_map.c
+++ b/src/hexen/am_map.c
@@ -29,6 +29,7 @@
 #include "i_video.h"
 #include "i_swap.h"
 #include "m_controls.h"
+#include "m_misc.h"
 #include "p_local.h"
 #include "am_map.h"
 #include "am_data.h"
@@ -1474,8 +1475,8 @@ void AM_DrawDeathmatchStats(void)
         else
         {
             MN_DrTextA(PlayerColorText[order[i]], 8, yPosition);
-            snprintf(textBuffer, sizeof(textBuffer),
-                     "%d", fragCount[order[i]]);
+            M_snprintf(textBuffer, sizeof(textBuffer),
+                       "%d", fragCount[order[i]]);
             MN_DrTextA(textBuffer, 80, yPosition);
             yPosition += 10;
         }
@@ -1509,19 +1510,19 @@ static void DrawWorldTimer(void)
     worldTimer -= minutes * 60;
     seconds = worldTimer;

-    snprintf(timeBuffer, sizeof(timeBuffer),
-             "%.2d : %.2d : %.2d", hours, minutes, seconds);
+    M_snprintf(timeBuffer, sizeof(timeBuffer),
+               "%.2d : %.2d : %.2d", hours, minutes, seconds);
     MN_DrTextA(timeBuffer, 240, 8);

     if (days)
     {
         if (days == 1)
         {
-            snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAY", days);
+            M_snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAY", days);
         }
         else
         {
-            snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAYS", days);
+            M_snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAYS", days);
         }
         MN_DrTextA(dayBuffer, 240, 20);
         if (days >= 5)
diff --git a/src/hexen/h2_main.c b/src/hexen/h2_main.c
index 607bc26b..a7c3dee3 100644
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -172,7 +172,7 @@ void D_BindVariables(void)
     {
         char buf[12];

-        snprintf(buf, sizeof(buf), "chatmacro%i", i);
+        M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
         M_BindVariable(buf, &chat_macros[i]);
     }
 }
@@ -190,7 +190,7 @@ static void D_SetDefaultSavePath(void)
     if (!strcmp(SavePath, ""))
     {
         SavePath = malloc(10);
-	snprintf(SavePath, 10, "hexndata%c", DIR_SEPARATOR);
+	M_snprintf(SavePath, 10, "hexndata%c", DIR_SEPARATOR);
     }
 }

@@ -632,7 +632,7 @@ void H2_GameLoop(void)
     if (M_CheckParm("-debugfile"))
     {
         char filename[20];
-        snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
+        M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
         debugfile = fopen(filename, "w");
     }
     I_SetWindowTitle("Hexen");
diff --git a/src/hexen/in_lude.c b/src/hexen/in_lude.c
index b1d0c4c9..fb1b1020 100644
--- a/src/hexen/in_lude.c
+++ b/src/hexen/in_lude.c
@@ -28,6 +28,7 @@
 #include "s_sound.h"
 #include "i_system.h"
 #include "i_video.h"
+#include "m_misc.h"
 #include "p_local.h"
 #include "v_video.h"

@@ -537,8 +538,8 @@ static void DrNumber(int val, int x, int y, int wrapThresh)

     if (!(val < -9 && wrapThresh < 1000))
     {
-        snprintf(buff, sizeof(buff), "%d",
-                 val >= wrapThresh ? val % wrapThresh : val);
+        M_snprintf(buff, sizeof(buff), "%d",
+                   val >= wrapThresh ? val % wrapThresh : val);
     }
     MN_DrTextA(buff, x - MN_TextAWidth(buff) / 2, y);
 }
@@ -555,8 +556,8 @@ static void DrNumberBold(int val, int x, int y, int wrapThresh)

     if (!(val < -9 && wrapThresh < 1000))
     {
-        snprintf(buff, sizeof(buff), "%d",
-                 val >= wrapThresh ? val % wrapThresh : val);
+        M_snprintf(buff, sizeof(buff), "%d",
+                   val >= wrapThresh ? val % wrapThresh : val);
     }
     MN_DrTextAYellow(buff, x - MN_TextAWidth(buff) / 2, y);
 }
diff --git a/src/hexen/mn_menu.c b/src/hexen/mn_menu.c
index d12d0dc3..75433306 100644
--- a/src/hexen/mn_menu.c
+++ b/src/hexen/mn_menu.c
@@ -685,7 +685,7 @@ static boolean ReadDescriptionForSlot(int slot, char *description)
     char name[100];
     char versionText[HXS_VERSION_TEXT_LENGTH];

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

     fp = fopen(name, "rb");

diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
index a9e64517..7ae6a985 100644
--- a/src/hexen/p_acs.c
+++ b/src/hexen/p_acs.c
@@ -443,8 +443,8 @@ boolean P_StartACS(int number, int map, byte * args, mobj_t * activator,
     if (infoIndex == -1)
     {                           // Script not found
         //I_Error("P_StartACS: Unknown script number %d", number);
-        snprintf(ErrorMsg, sizeof(ErrorMsg),
-                 "P_STARTACS ERROR: UNKNOWN SCRIPT %d", number);
+        M_snprintf(ErrorMsg, sizeof(ErrorMsg),
+                   "P_STARTACS ERROR: UNKNOWN SCRIPT %d", number);
         P_SetMessage(&players[consoleplayer], ErrorMsg, true);
     }
     statePtr = &ACSInfo[infoIndex].state;
@@ -540,8 +540,8 @@ boolean P_StartLockedACS(line_t * line, byte * args, mobj_t * mo, int side)
     {
         if (!(mo->player->keys & (1 << (lock - 1))))
         {
-            snprintf(LockedBuffer, sizeof(LockedBuffer),
-                     "YOU NEED THE %s\n", TextKeyMessages[lock - 1]);
+            M_snprintf(LockedBuffer, sizeof(LockedBuffer),
+                       "YOU NEED THE %s\n", TextKeyMessages[lock - 1]);
             P_SetMessage(mo->player, LockedBuffer, true);
             S_StartSound(mo, SFX_DOOR_LOCKED);
             return false;
@@ -1692,7 +1692,7 @@ static int CmdPrintNumber(void)
 {
     char tempStr[16];

-    snprintf(tempStr, sizeof(tempStr), "%d", Pop());
+    M_snprintf(tempStr, sizeof(tempStr), "%d", Pop());
     M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer));
     return SCRIPT_CONTINUE;
 }
diff --git a/src/hexen/p_setup.c b/src/hexen/p_setup.c
index cd075651..181e19fe 100644
--- a/src/hexen/p_setup.c
+++ b/src/hexen/p_setup.c
@@ -700,7 +700,7 @@ void P_SetupLevel(int episode, int map, int playermask, skill_t skill)
     P_InitThinkers();
     leveltime = 0;

-    snprintf(lumpname, sizeof(lumpname), "MAP%02d", map);
+    M_snprintf(lumpname, sizeof(lumpname), "MAP%02d", map);
     lumpnum = W_GetNumForName(lumpname);
     //
     // Begin processing map lumps
diff --git a/src/hexen/p_spec.c b/src/hexen/p_spec.c
index 06a4a6ff..58f3defe 100644
--- a/src/hexen/p_spec.c
+++ b/src/hexen/p_spec.c
@@ -27,6 +27,7 @@

 #include "h2def.h"
 #include "i_system.h"
+#include "m_misc.h"
 #include "p_local.h"
 #include "s_sound.h"

@@ -428,8 +429,8 @@ static boolean CheckedLockedDoor(mobj_t * mo, byte lock)
     }
     if (!(mo->player->keys & (1 << (lock - 1))))
     {
-        snprintf(LockedBuffer, sizeof(LockedBuffer),
-                 "YOU NEED THE %s\n", TextKeyMessages[lock - 1]);
+        M_snprintf(LockedBuffer, sizeof(LockedBuffer),
+                   "YOU NEED THE %s\n", TextKeyMessages[lock - 1]);
         P_SetMessage(mo->player, LockedBuffer, true);
         S_StartSound(mo, SFX_DOOR_LOCKED);
         return false;
diff --git a/src/hexen/sb_bar.c b/src/hexen/sb_bar.c
index b35c1f51..5d2438aa 100644
--- a/src/hexen/sb_bar.c
+++ b/src/hexen/sb_bar.c
@@ -640,20 +640,20 @@ static void DrawSoundInfo(void)
             MN_DrTextA("------", xPos[0], y);
             continue;
         }
-        snprintf(text, sizeof(text), "%s", c->name);
+        M_snprintf(text, sizeof(text), "%s", c->name);
         M_ForceUppercase(text);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->type);
+        M_snprintf(text, sizeof(text), "%d", c->mo->type);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS);
+        M_snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS);
+        M_snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", (int) c->id);
+        M_snprintf(text, sizeof(text), "%d", (int) c->id);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->priority);
+        M_snprintf(text, sizeof(text), "%d", c->priority);
         MN_DrTextA(text, xPos[x++], y);
-        snprintf(text, sizeof(text), "%d", c->distance);
+        M_snprintf(text, sizeof(text), "%d", c->distance);
         MN_DrTextA(text, xPos[x++], y);
     }
     UpdateState |= I_FULLSCRN;
@@ -1738,7 +1738,7 @@ static void CheatWarpFunc(player_t * player, Cheat_t * cheat)
         P_SetMessage(player, TXT_CHEATBADINPUT, true);
         return;
     }
-    snprintf(mapName, sizeof(mapName), "MAP%02d", map);
+    M_snprintf(mapName, sizeof(mapName), "MAP%02d", map);
     if (W_CheckNumForName(mapName) == -1)
     {                       // Can't find
         P_SetMessage(player, TXT_CHEATNOMAP, true);
@@ -1769,7 +1769,7 @@ static void CheatMassacreFunc(player_t * player, Cheat_t * cheat)
     char buffer[80];

     count = P_Massacre();
-    snprintf(buffer, sizeof(buffer), "%d MONSTERS KILLED\n", count);
+    M_snprintf(buffer, sizeof(buffer), "%d MONSTERS KILLED\n", count);
     P_SetMessage(player, buffer, true);
 }

@@ -1857,12 +1857,12 @@ static void CheatVersionFunc(player_t * player, Cheat_t * cheat)
 static void CheatDebugFunc(player_t * player, Cheat_t * cheat)
 {
     char textBuffer[50];
-    snprintf(textBuffer, sizeof(textBuffer),
-             "MAP %d (%d)  X:%5d  Y:%5d  Z:%5d",
-             P_GetMapWarpTrans(gamemap),
-             gamemap,
-             player->mo->x >> FRACBITS,
-             player->mo->y >> FRACBITS, player->mo->z >> FRACBITS);
+    M_snprintf(textBuffer, sizeof(textBuffer),
+               "MAP %d (%d)  X:%5d  Y:%5d  Z:%5d",
+               P_GetMapWarpTrans(gamemap),
+               gamemap,
+               player->mo->x >> FRACBITS,
+               player->mo->y >> FRACBITS, player->mo->z >> FRACBITS);
     P_SetMessage(player, textBuffer, true);
 }

@@ -1897,8 +1897,8 @@ static void CheatScriptFunc3(player_t * player, Cheat_t * cheat)

     if (P_StartACS(script, 0, script_args, player->mo, NULL, 0))
     {
-        snprintf(textBuffer, sizeof(textBuffer),
-                 "RUNNING SCRIPT %.2d", script);
+        M_snprintf(textBuffer, sizeof(textBuffer),
+                   "RUNNING SCRIPT %.2d", script);
         P_SetMessage(player, textBuffer, true);
     }
 }
@@ -1930,8 +1930,8 @@ static void CheatTrackFunc1(player_t * player, Cheat_t * cheat)
         P_SetMessage(player, "ERROR INITIALIZING CD", true);
     }

-    snprintf(buffer, sizeof(buffer), "ENTER DESIRED CD TRACK (%.2d - %.2d):\n",
-            I_CDMusFirstTrack(), I_CDMusLastTrack());
+    M_snprintf(buffer, sizeof(buffer), "ENTER DESIRED CD TRACK (%.2d - %.2d):\n",
+               I_CDMusFirstTrack(), I_CDMusLastTrack());
     P_SetMessage(player, buffer, true);
 }

@@ -1968,14 +1968,14 @@ static void CheatTrackFunc2(player_t * player, Cheat_t * cheat)

     if (!S_StartCustomCDTrack(track))
     {
-        snprintf(buffer, sizeof(buffer),
-                 "ERROR WHILE TRYING TO PLAY CD TRACK: %.2d\n", track);
+        M_snprintf(buffer, sizeof(buffer),
+                   "ERROR WHILE TRYING TO PLAY CD TRACK: %.2d\n", track);
         P_SetMessage(player, buffer, true);
     }
     else
     {
         // No error encountered while attempting to play the track
-        snprintf(buffer, sizeof(buffer), "PLAYING TRACK: %.2d\n", track);
+        M_snprintf(buffer, sizeof(buffer), "PLAYING TRACK: %.2d\n", track);
         P_SetMessage(player, buffer, true);
     }
 }
diff --git a/src/hexen/sc_man.c b/src/hexen/sc_man.c
index 35f5ec0b..bf3574c3 100644
--- a/src/hexen/sc_man.c
+++ b/src/hexen/sc_man.c
@@ -88,7 +88,7 @@ void SC_Open(char *name)

     if (sc_FileScripts == true)
     {
-        snprintf(fileName, sizeof(fileName), "%s%s.txt", sc_ScriptsDir, name);
+        M_snprintf(fileName, sizeof(fileName), "%s%s.txt", sc_ScriptsDir, name);
         SC_OpenFile(fileName);
     }
     else
diff --git a/src/hexen/sv_save.c b/src/hexen/sv_save.c
index dd707398..1aecc5cc 100644
--- a/src/hexen/sv_save.c
+++ b/src/hexen/sv_save.c
@@ -1940,7 +1940,7 @@ void SV_SaveGame(int slot, char *description)
     unsigned int i;

     // Open the output file
-    snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);
+    M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);
     OpenStreamOut(fileName);

     // Write game save description
@@ -2000,7 +2000,7 @@ void SV_SaveMap(boolean savePlayers)
     SavingPlayers = savePlayers;

     // Open the output file
-    snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
+    M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
     OpenStreamOut(fileName);

     // Place a header marker
@@ -2048,7 +2048,7 @@ void SV_LoadGame(int slot)
     }

     // Create the name
-    snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);
+    M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath);

     // Load the file
     M_ReadFile(fileName, &SaveBuffer);
@@ -2192,7 +2192,7 @@ void SV_MapTeleport(int map, int position)
     TargetPlayerAddrs = NULL;

     gamemap = map;
-    snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
+    M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
     if (!deathmatch && ExistingFile(fileName))
     {                           // Unarchive map
         SV_LoadMap();
@@ -2342,7 +2342,7 @@ boolean SV_RebornSlotAvailable(void)
 {
     char fileName[100];

-    snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, REBORN_SLOT);
+    M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, REBORN_SLOT);
     return ExistingFile(fileName);
 }

@@ -2363,7 +2363,7 @@ void SV_LoadMap(void)
     RemoveAllThinkers();

     // Create the name
-    snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);
+    M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap);

     // Load the file
     M_ReadFile(fileName, &SaveBuffer);
@@ -3204,11 +3204,11 @@ static void ClearSaveSlot(int slot)

     for (i = 0; i < MAX_MAPS; i++)
     {
-        snprintf(fileName, sizeof(fileName),
-                 "%shex%d%02d.hxs", SavePath, slot, i);
+        M_snprintf(fileName, sizeof(fileName),
+                   "%shex%d%02d.hxs", SavePath, slot, i);
         remove(fileName);
     }
-    snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, slot);
+    M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, slot);
     remove(fileName);
 }

@@ -3228,21 +3228,21 @@ static void CopySaveSlot(int sourceSlot, int destSlot)

     for (i = 0; i < MAX_MAPS; i++)
     {
-        snprintf(sourceName, sizeof(sourceName),
-                 "%shex%d%02d.hxs", SavePath, sourceSlot, i);
+        M_snprintf(sourceName, sizeof(sourceName),
+                   "%shex%d%02d.hxs", SavePath, sourceSlot, i);
         if (ExistingFile(sourceName))
         {
-            snprintf(destName, sizeof(destName),
-                     "%shex%d%02d.hxs", SavePath, destSlot, i);
+            M_snprintf(destName, sizeof(destName),
+                       "%shex%d%02d.hxs", SavePath, destSlot, i);
             CopyFile(sourceName, destName);
         }
     }
-    snprintf(sourceName, sizeof(sourceName),
-             "%shex%d.hxs", SavePath, sourceSlot);
+    M_snprintf(sourceName, sizeof(sourceName),
+               "%shex%d.hxs", SavePath, sourceSlot);
     if (ExistingFile(sourceName))
     {
-        snprintf(destName, sizeof(destName),
-                 "%shex%d.hxs", SavePath, destSlot);
+        M_snprintf(destName, sizeof(destName),
+                   "%shex%d.hxs", SavePath, destSlot);
         CopyFile(sourceName, destName);
     }
 }
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index 379537dd..142e401a 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -1527,11 +1527,11 @@ void I_OPL_DevMessages(char *result, size_t result_len)

     if (num_tracks == 0)
     {
-        snprintf(result, result_len, "No OPL track!");
+        M_snprintf(result, result_len, "No OPL track!");
         return;
     }

-    snprintf(result, result_len, "Tracks:\n");
+    M_snprintf(result, result_len, "Tracks:\n");
     lines = 1;

     for (i = 0; i < NumActiveChannels(); ++i)
@@ -1543,18 +1543,18 @@ void I_OPL_DevMessages(char *result, size_t result_len)

         instr_num = tracks[0].channels[i].instrument - main_instrs;

-        snprintf(tmp, sizeof(tmp),
-                "chan %i: %c i#%i (%s)\n",
-                i,
-                ChannelInUse(&tracks[0].channels[i]) ? '\'' : ' ',
-                instr_num + 1,
-                main_instr_names[instr_num]);
+        M_snprintf(tmp, sizeof(tmp),
+                   "chan %i: %c i#%i (%s)\n",
+                   i,
+                   ChannelInUse(&tracks[0].channels[i]) ? '\'' : ' ',
+                   instr_num + 1,
+                   main_instr_names[instr_num]);
         M_StringConcat(result, tmp, result_len);

         ++lines;
     }

-    snprintf(tmp, sizeof(tmp), "\nLast percussion:\n");
+    M_snprintf(tmp, sizeof(tmp), "\nLast percussion:\n");
     M_StringConcat(result, tmp, result_len);
     lines += 2;

@@ -1566,11 +1566,11 @@ void I_OPL_DevMessages(char *result, size_t result_len)
             break;
         }

-        snprintf(tmp, sizeof(tmp),
-                 "%cp#%i (%s)\n",
-                 i == 0 ? '\'' : ' ',
-                 last_perc[i],
-                 percussion_names[last_perc[i] - 35]);
+        M_snprintf(tmp, sizeof(tmp),
+                   "%cp#%i (%s)\n",
+                   i == 0 ? '\'' : ' ',
+                   last_perc[i],
+                   percussion_names[last_perc[i] - 35]);
         M_StringConcat(result, tmp, result_len);
         ++lines;

diff --git a/src/i_pcsound.c b/src/i_pcsound.c
index 0303d2e4..bcbf16d4 100644
--- a/src/i_pcsound.c
+++ b/src/i_pcsound.c
@@ -253,7 +253,7 @@ static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx)

     if (use_sfx_prefix)
     {
-        snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name));
+        M_snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name));
     }
     else
     {
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index 299a3ff4..2a1d83bc 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -705,8 +705,8 @@ static boolean CacheSFX(sfxinfo_t *sfxinfo)
     {
         char filename[16];

-        snprintf(filename, sizeof(filename), "%s.wav",
-                 DEH_String(S_sfx[sound].name));
+        M_snprintf(filename, sizeof(filename), "%s.wav",
+                   DEH_String(S_sfx[sound].name));
         WriteWAV(filename, sound_chunks[sound].abuf,
                  sound_chunks[sound].alen, mixer_freq);
     }
@@ -733,7 +733,7 @@ static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len)

     if (use_sfx_prefix)
     {
-        snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name));
+        M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name));
     }
     else
     {
diff --git a/src/i_system.c b/src/i_system.c
index 5e3ae89e..041217b0 100644
--- a/src/i_system.c
+++ b/src/i_system.c
@@ -318,7 +318,7 @@ void I_Error (char *error, ...)

         va_start(argptr, error);
         memset(msgbuf, 0, sizeof(msgbuf));
-        vsnprintf(msgbuf, sizeof(msgbuf) - 1, error, argptr);
+        M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr);
         va_end(argptr);

         MultiByteToWideChar(CP_ACP, 0,
@@ -338,7 +338,7 @@ void I_Error (char *error, ...)

         va_start(argptr, error);
         memset(msgbuf, 0, sizeof(msgbuf));
-        vsnprintf(msgbuf, sizeof(msgbuf) - 1, error, argptr);
+        M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr);
         va_end(argptr);

 	// The CoreFoundation message box wraps text lines, so replace
diff --git a/src/i_video.c b/src/i_video.c
index 823209b8..7c739c17 100644
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -1820,7 +1820,7 @@ static void SetWindowPositionVars(void)
     }
     else if (sscanf(window_position, "%i,%i", &x, &y) == 2)
     {
-        snprintf(buf, sizeof(buf), "SDL_VIDEO_WINDOW_POS=%i,%i", x, y);
+        M_snprintf(buf, sizeof(buf), "SDL_VIDEO_WINDOW_POS=%i,%i", x, y);
         putenv(buf);
     }
 }
@@ -1990,7 +1990,7 @@ void I_InitGraphics(void)
         int winid;

         sscanf(env, "0x%x", &winid);
-        snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid);
+        M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid);

         putenv(winenv);
     }
diff --git a/src/m_controls.c b/src/m_controls.c
index 792c7406..73e81b1b 100644
--- a/src/m_controls.c
+++ b/src/m_controls.c
@@ -28,6 +28,7 @@
 #include "doomkeys.h"

 #include "m_config.h"
+#include "m_misc.h"

 //
 // Keyboard controls
@@ -385,7 +386,7 @@ void M_BindChatControls(unsigned int num_players)

     for (i=0; i<num_players; ++i)
     {
-        snprintf(name, sizeof(name), "key_multi_msgplayer%i", i + 1);
+        M_snprintf(name, sizeof(name), "key_multi_msgplayer%i", i + 1);
         M_BindVariable(name, &key_multi_msgplayer[i]);
     }
 }
diff --git a/src/m_misc.c b/src/m_misc.c
index 15daee3b..c280661f 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -449,6 +449,39 @@ char *M_StringJoin(const char *s, ...)
     return result;
 }

+// On Windows, vsnprintf() is _vsnprintf().
+#ifdef _WIN32
+#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+// Safe, portable vsnprintf().
+int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
+{
+    if (buf_len < 1)
+    {
+        return 0;
+    }
+
+    // Windows (and other OSes?) has a vsnprintf() that doesn't always
+    // append a trailing \0. So we must do it, and write into a buffer
+    // that is one byte shorter; otherwise this function is unsafe.
+    buf[buf_len - 1] = '\0';
+    return vsnprintf(buf, buf_len - 1, s, args);
+}
+
+// Safe, portable snprintf().
+int M_snprintf(char *buf, size_t buf_len, const char *s, ...)
+{
+    va_list args;
+    int result;
+    va_start(args, s);
+    result = M_vsnprintf(buf, buf_len, s, args);
+    va_end(args);
+    return result;
+}
+
 #ifdef _WIN32

 char *M_OEMToUTF8(const char *oem)
diff --git a/src/m_misc.h b/src/m_misc.h
index cb3dc8f6..681605bf 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -28,8 +28,8 @@
 #ifndef __M_MISC__
 #define __M_MISC__

-
 #include <stdio.h>
+#include <stdarg.h>

 #include "doomtype.h"

@@ -50,6 +50,8 @@ char *M_StringReplace(const char *haystack, const char *needle,
 char *M_StringJoin(const char *s, ...);
 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, ...);
 char *M_OEMToUTF8(const char *ansi);

 #endif
diff --git a/src/net_gui.c b/src/net_gui.c
index 41ef7d3b..80c606b1 100644
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -34,6 +34,7 @@
 #include "i_system.h"
 #include "i_timer.h"
 #include "i_video.h"
+#include "m_misc.h"

 #include "net_client.h"
 #include "net_gui.h"
@@ -101,7 +102,7 @@ static void BuildWindow(void)

     for (i = 0; i < net_client_wait_data.max_players; ++i)
     {
-        snprintf(buf, sizeof(buf), " %i. ", i + 1);
+        M_snprintf(buf, sizeof(buf), " %i. ", i + 1);
         TXT_AddWidget(table, TXT_NewLabel(buf));
         player_labels[i] = TXT_NewLabel("");
         ip_labels[i] = TXT_NewLabel("");
@@ -164,8 +165,8 @@ static void UpdateGUI(void)

     if (net_client_wait_data.num_drones > 0)
     {
-        snprintf(buf, sizeof(buf), " (+%i observer clients)",
-                 net_client_wait_data.num_drones);
+        M_snprintf(buf, sizeof(buf), " (+%i observer clients)",
+                   net_client_wait_data.num_drones);
         TXT_SetLabel(drone_label, buf);
     }
     else
diff --git a/src/net_loop.c b/src/net_loop.c
index acdc2cb6..cff0f37a 100644
--- a/src/net_loop.c
+++ b/src/net_loop.c
@@ -28,6 +28,7 @@

 #include "doomtype.h"
 #include "i_system.h"
+#include "m_misc.h"
 #include "net_defs.h"
 #include "net_loop.h"
 #include "net_packet.h"
@@ -128,7 +129,7 @@ static boolean NET_CL_RecvPacket(net_addr_t **addr, net_packet_t **packet)

 static void NET_CL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len)
 {
-    snprintf(buffer, buffer_len, "local server");
+    M_snprintf(buffer, buffer_len, "local server");
 }

 static void NET_CL_FreeAddress(net_addr_t *addr)
@@ -204,7 +205,7 @@ static boolean NET_SV_RecvPacket(net_addr_t **addr, net_packet_t **packet)

 static void NET_SV_AddrToString(net_addr_t *addr, char *buffer, int buffer_len)
 {
-    snprintf(buffer, buffer_len, "local client");
+    M_snprintf(buffer, buffer_len, "local client");
 }

 static void NET_SV_FreeAddress(net_addr_t *addr)
diff --git a/src/net_sdl.c b/src/net_sdl.c
index de86172f..e27e4da5 100644
--- a/src/net_sdl.c
+++ b/src/net_sdl.c
@@ -30,6 +30,7 @@
 #include "doomtype.h"
 #include "i_system.h"
 #include "m_argv.h"
+#include "m_misc.h"
 #include "net_defs.h"
 #include "net_io.h"
 #include "net_packet.h"
@@ -312,12 +313,12 @@ void NET_SDL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len)

     ip = (IPaddress *) addr->handle;

-    snprintf(buffer, buffer_len,
-             "%i.%i.%i.%i",
-             ip->host & 0xff,
-             (ip->host >> 8) & 0xff,
-             (ip->host >> 16) & 0xff,
-             (ip->host >> 24) & 0xff);
+    M_snprintf(buffer, buffer_len,
+               "%i.%i.%i.%i",
+               ip->host & 0xff,
+               (ip->host >> 8) & 0xff,
+               (ip->host >> 16) & 0xff,
+               (ip->host >> 24) & 0xff);
 }

 net_addr_t *NET_SDL_ResolveAddress(char *address)
diff --git a/src/net_server.c b/src/net_server.c
index c22d57e9..ef0ab7b5 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -200,7 +200,7 @@ static void NET_SV_SendConsoleMessage(net_client_t *client, char *s, ...)
     net_packet_t *packet;

     va_start(args, s);
-    vsnprintf(buf, sizeof(buf), s, args);
+    M_vsnprintf(buf, sizeof(buf), s, args);
     va_end(args);

     packet = NET_Conn_NewReliable(&client->connection,
@@ -218,7 +218,7 @@ static void NET_SV_BroadcastMessage(char *s, ...)
     int i;

     va_start(args, s);
-    vsnprintf(buf, sizeof(buf), s, args);
+    M_vsnprintf(buf, sizeof(buf), s, args);
     va_end(args);

     for (i=0; i<MAXNETNODES; ++i)
diff --git a/src/setup/txt_mouseinput.c b/src/setup/txt_mouseinput.c
index a69d35b4..aec7d5d9 100644
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -90,7 +90,7 @@ static void GetMouseButtonDescription(int button, char *buf, size_t buf_len)
             M_StringCopy(buf, "MID", buf_len);
             break;
         default:
-            snprintf(buf, buf_len, "BUTTON #%i", button + 1);
+            M_snprintf(buf, buf_len, "BUTTON #%i", button + 1);
             break;
     }
 }
diff --git a/src/strife/am_map.c b/src/strife/am_map.c
index 05f8a519..b1336d20 100644
--- a/src/strife/am_map.c
+++ b/src/strife/am_map.c
@@ -32,6 +32,7 @@
 #include "z_zone.h"
 #include "doomkeys.h"
 #include "doomdef.h"
+#include "m_misc.h"
 #include "st_stuff.h"
 #include "p_local.h"
 #include "w_wad.h"
@@ -670,8 +671,8 @@ AM_Responder
         }
         else if (key == key_map_mark)
         {
-            snprintf(buffer, sizeof(buffer),
-                     "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum);
+            M_snprintf(buffer, sizeof(buffer),
+                       "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum);
             plr->message = buffer;
             AM_addMark();
         }
diff --git a/src/strife/d_main.c b/src/strife/d_main.c
index e419fd99..db5e40f2 100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -461,7 +461,7 @@ void D_BindVariables(void)
     {
         char buf[12];

-        snprintf(buf, sizeof(buf), "chatmacro%i", i);
+        M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
         M_BindVariable(buf, &chat_macros[i]);
     }
 }
@@ -792,8 +792,8 @@ static char *GetGameName(char *gamename)

             gamename_size = strlen(deh_sub) + 10;
             gamename = Z_Malloc(gamename_size, PU_STATIC, 0);
-            snprintf(gamename, gamename_size, deh_sub,
-                     STRIFE_VERSION / 100, STRIFE_VERSION % 100);
+            M_snprintf(gamename, gamename_size, deh_sub,
+                       STRIFE_VERSION / 100, STRIFE_VERSION % 100);

             while (gamename[0] != '\0' && isspace(gamename[0]))
             {
diff --git a/src/strife/g_game.c b/src/strife/g_game.c
index 23e0cef9..c67d7be1 100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -985,8 +985,8 @@ void G_Ticker (void)
             {
                 static char turbomessage[80];
                 extern char player_names[8][16];
-                snprintf(turbomessage, sizeof(turbomessage),
-                         "%s is turbo!", player_names[i]);
+                M_snprintf(turbomessage, sizeof(turbomessage),
+                           "%s is turbo!", player_names[i]);
                 players[consoleplayer].message = turbomessage;
                 turbodetected[i] = false;
             }
@@ -1290,7 +1290,7 @@ void G_LoadPath(int map)
     char mapbuf[33];

     memset(mapbuf, 0, sizeof(mapbuf));
-    snprintf(mapbuf, sizeof(mapbuf), "%d", map);
+    M_snprintf(mapbuf, sizeof(mapbuf), "%d", map);

     // haleyjd: free if already set, and use M_SafeFilePath
     if(loadpath)
@@ -1805,7 +1805,7 @@ void G_DoSaveGame (char *path)

     // [STRIFE] custom save file path logic
     memset(gamemapstr, 0, sizeof(gamemapstr));
-    snprintf(gamemapstr, sizeof(gamemapstr), "%d", gamemap);
+    M_snprintf(gamemapstr, sizeof(gamemapstr), "%d", gamemap);
     savegame_file = M_SafeFilePath(path, gamemapstr);

     // [STRIFE] write the "current" file, which tells which hub map
@@ -1870,7 +1870,7 @@ void G_DoSaveGame (char *path)
     // [STRIFE]: custom message logic
     if(!strcmp(path, savepath))
     {
-        snprintf(savename, sizeof(savename), "%s saved.", character_name);
+        M_snprintf(savename, sizeof(savename), "%s saved.", character_name);
         players[consoleplayer].message = savename;
     }

@@ -2195,7 +2195,7 @@ void G_RecordDemo (char* name)
     usergame = false;
     demoname_size = strlen(name) + 5;
     demoname = Z_Malloc(demoname_size, PU_STATIC, NULL);
-    snprintf(demoname, demoname_size, "%s.lmp", name);
+    M_snprintf(demoname, demoname_size, "%s.lmp", name);
     maxsize = 0x20000;

     //!
@@ -2290,8 +2290,8 @@ static char *DemoVersionDescription(int version)
     }

     // Unknown version. Who knows?
-    snprintf(resultbuf, sizeof(resultbuf),
-             "%i.%i (unknown)", version / 100, version % 100);
+    M_snprintf(resultbuf, sizeof(resultbuf),
+               "%i.%i (unknown)", version / 100, version % 100);

     return resultbuf;
 }
diff --git a/src/strife/m_saves.c b/src/strife/m_saves.c
index 4303dbe2..e4d5919f 100644
--- a/src/strife/m_saves.c
+++ b/src/strife/m_saves.c
@@ -222,7 +222,7 @@ void M_SaveMoveMapToHere(void)
     char tmpnum[33];

     // haleyjd: no itoa available...
-    snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap);
+    M_snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap);

     // haleyjd: use M_SafeFilePath, not sprintf
     mapsave  = M_SafeFilePath(savepath, tmpnum);
@@ -251,7 +251,7 @@ void M_SaveMoveHereToMap(void)
     char tmpnum[33];

     // haleyjd: no itoa available...
-    snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap);
+    M_snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap);

     mapsave  = M_SafeFilePath(savepathtemp, tmpnum);
     heresave = M_SafeFilePath(savepathtemp, "here");
@@ -437,7 +437,7 @@ char *M_SafeFilePath(const char *basepath, const char *newcomponent)
     // that either basepath or newcomponent includes a redundant slash at the
     // end or beginning respectively.
     newstrlen = M_StringAlloc(&newstr, 3, 1, basepath, "/", newcomponent);
-    snprintf(newstr, newstrlen, "%s/%s", basepath, newcomponent);
+    M_snprintf(newstr, newstrlen, "%s/%s", basepath, newcomponent);
     M_NormalizeSlashes(newstr);

     return newstr;
@@ -477,8 +477,8 @@ char *M_MakeStrifeSaveDir(int slotnum, const char *extra)
 {
     static char tmpbuffer[32];

-    snprintf(tmpbuffer, sizeof(tmpbuffer),
-             "strfsav%d.ssg%s", slotnum, extra);
+    M_snprintf(tmpbuffer, sizeof(tmpbuffer),
+               "strfsav%d.ssg%s", slotnum, extra);

     return tmpbuffer;
 }
diff --git a/src/strife/p_inter.c b/src/strife/p_inter.c
index 00b0ef24..96594211 100644
--- a/src/strife/p_inter.c
+++ b/src/strife/p_inter.c
@@ -971,8 +971,8 @@ void P_KillMobj(mobj_t* source, mobj_t* target)
         EV_DoDoor(&junk, close);
         P_NoiseAlert(players[0].mo, players[0].mo);

-        snprintf(plrkilledmsg, sizeof(plrkilledmsg),
-                 "%s", DEH_String("You're dead!  You set off the alarm."));
+        M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
+                   "%s", DEH_String("You're dead!  You set off the alarm."));
         if(!deathmatch)
             players[consoleplayer].message = plrkilledmsg;

@@ -1011,8 +1011,8 @@ void P_KillMobj(mobj_t* source, mobj_t* target)
     case MT_TOKEN_ALARM:
         P_NoiseAlert(players[0].mo, players[0].mo);

-        snprintf(plrkilledmsg, sizeof(plrkilledmsg),
-                 "%s", DEH_String("You Fool!  You've set off the alarm"));
+        M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
+                   "%s", DEH_String("You Fool!  You've set off the alarm"));
         if(!deathmatch)
             players[consoleplayer].message = plrkilledmsg;
         return;
diff --git a/src/strife/p_saveg.c b/src/strife/p_saveg.c
index 47fb7e8c..2f59c732 100644
--- a/src/strife/p_saveg.c
+++ b/src/strife/p_saveg.c
@@ -32,6 +32,7 @@
 #include "deh_main.h"
 #include "i_system.h"
 #include "z_zone.h"
+#include "m_misc.h"
 #include "p_local.h"
 #include "p_saveg.h"

@@ -80,7 +81,7 @@ char *P_SaveGameFile(int slot)

     DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot);

-    snprintf(filename, filename_size, "%s%s", savegamedir, basename);
+    M_snprintf(filename, filename_size, "%s%s", savegamedir, basename);

     return filename;
 }
@@ -1609,7 +1610,7 @@ void P_WriteSaveGameHeader(char *description)
     */

     memset (name,0,sizeof(name));
-    snprintf(name, sizeof(name), "ver %i", STRIFE_VERSION);
+    M_snprintf(name, sizeof(name), "ver %i", STRIFE_VERSION);

     for (i=0; i<VERSIONSIZE; ++i)
         saveg_write8(name[i]);
@@ -1649,7 +1650,7 @@ boolean P_ReadSaveGameHeader(void)
         read_vcheck[i] = saveg_read8();

     memset (vcheck,0,sizeof(vcheck));
-    snprintf(vcheck, sizeof(vcheck), "ver %i", STRIFE_VERSION);
+    M_snprintf(vcheck, sizeof(vcheck), "ver %i", STRIFE_VERSION);
     if (strcmp(read_vcheck, vcheck) != 0)
         return false;                       // bad version

diff --git a/src/strife/p_user.c b/src/strife/p_user.c
index d2f8b8e3..e24e40fe 100644
--- a/src/strife/p_user.c
+++ b/src/strife/p_user.c
@@ -39,6 +39,7 @@
 #include "z_zone.h"
 #include "w_wad.h"
 #include "p_pspr.h"
+#include "m_misc.h"
 #include "m_random.h"
 #include "s_sound.h"
 #include "p_inter.h"
@@ -861,8 +862,8 @@ boolean P_UseInventoryItem(player_t* player, int item)
         if(name == NULL)
             name = "Item";

-        snprintf(useinventorymsg, sizeof(useinventorymsg),
-                 "You used the %s.", name);
+        M_snprintf(useinventorymsg, sizeof(useinventorymsg),
+                   "You used the %s.", name);
         player->message = useinventorymsg;

         if(player == &players[consoleplayer])
diff --git a/src/strife/s_sound.c b/src/strife/s_sound.c
index 861586fe..fb1fc44f 100644
--- a/src/strife/s_sound.c
+++ b/src/strife/s_sound.c
@@ -788,7 +788,7 @@ void S_ChangeMusic(int musicnum, int looping)
     // get lumpnum if neccessary
     if (!music->lumpnum)
     {
-        snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name));
+        M_snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name));
         music->lumpnum = W_GetNumForName(namebuf);
     }

diff --git a/src/strife/st_stuff.c b/src/strife/st_stuff.c
index 34682093..dc6db608 100644
--- a/src/strife/st_stuff.c
+++ b/src/strife/st_stuff.c
@@ -54,6 +54,7 @@
 #include "am_map.h"
 #include "m_cheat.h"
 #include "m_menu.h" // villsa [STRIFE]
+#include "m_misc.h"

 #include "s_sound.h"

@@ -663,11 +664,11 @@ boolean ST_Responder(event_t* ev)
     {
         // [STRIFE] 'GPS' for player position
         static char buf[ST_MSGWIDTH];
-        snprintf(buf, sizeof(buf),
-                "ang=0x%x;x,y=(0x%x,0x%x)",
-                players[consoleplayer].mo->angle,
-                players[consoleplayer].mo->x,
-                players[consoleplayer].mo->y);
+        M_snprintf(buf, sizeof(buf),
+                   "ang=0x%x;x,y=(0x%x,0x%x)",
+                   players[consoleplayer].mo->angle,
+                   players[consoleplayer].mo->x,
+                   players[consoleplayer].mo->y);
         plyr->message = buf;
     }

diff --git a/src/v_video.c b/src/v_video.c
index d8934456..ac275ec6 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -818,7 +818,7 @@ void V_ScreenShot(char *format)

     for (i=0; i<=99; i++)
     {
-        snprintf(lbmname, sizeof(lbmname), format, i, ext);
+        M_snprintf(lbmname, sizeof(lbmname), format, i, ext);

         if (!M_FileExists(lbmname))
         {