shithub: choc

Download patch

ref: 0abcb12fbbf199fa561da03794326e242b94018d
parent: 5e6377aa3bd2c7d4999a180c8131284b94b50457
author: Fabian Greffrath <fabian@greffrath.com>
date: Fri May 24 10:24:21 EDT 2019

allow for loading WAD files and DEHACKED patches by drag-and-drop (take 3)

Compose a proper command line from loose file paths passed as
arguments. This allows for loading WAD files and DEHACKED patches by
dragging them onto the game executable in e.g. Windows Explorer.

Completely new implementation turned into a sorting problem and
extended with some heuristics to make sure the command line only
consists of fully-qualified path names.

Fixes #1161.

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,8 +35,11 @@
 
 DEDSERV_FILES=\
 d_dedicated.c                              \
+d_iwad.c             d_iwad.h              \
 d_mode.c             d_mode.h              \
+deh_str.c            deh_str.h             \
 i_timer.c            i_timer.h             \
+m_config.c           m_config.h            \
 net_common.c         net_common.h          \
 net_dedicated.c      net_dedicated.h       \
 net_io.c             net_io.h              \
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -50,6 +50,21 @@
     { "strife1.wad",  strife,    commercial, "Strife" },
 };
 
+boolean D_IsIWADName(const char *name)
+{
+    int i;
+
+    for (i = 0; i < arrlen(iwads); i++)
+    {
+        if (!strcasecmp(name, iwads[i].name))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 // Array of locations to search for IWAD files
 //
 // "128 IWAD search directories should be enough for anybody".
--- a/src/d_iwad.h
+++ b/src/d_iwad.h
@@ -39,6 +39,7 @@
     const char *description;
 } iwad_t;
 
+boolean D_IsIWADName(const char *name);
 char *D_FindWADByName(const char *filename);
 char *D_TryFindWADByName(const char *filename);
 char *D_FindIWAD(int mask, GameMission_t *mission);
--- a/src/i_main.c
+++ b/src/i_main.c
@@ -49,6 +49,12 @@
         exit(0);
     }
 
+#if defined(_WIN32)
+    // compose a proper command line from loose file paths passed as arguments
+    // to allow for loading WADs and DEHACKED patches by drag-and-drop
+    M_AddLooseFiles();
+#endif
+
     M_FindResponseFile();
 
     #ifdef SDL_HINT_NO_SIGNAL_HANDLERS
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -21,7 +21,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "SDL_stdinc.h"
+
 #include "doomtype.h"
+#include "d_iwad.h"
 #include "i_system.h"
 #include "m_misc.h"
 #include "m_argv.h"  // haleyjd 20110212: warning fix
@@ -260,6 +263,152 @@
         LoadResponseFile(i + 1, myargv[i + 1]);
     }
 }
+
+#if defined(_WIN32)
+enum
+{
+    FILETYPE_UNKNOWN = 0x0,
+    FILETYPE_IWAD =    0x2,
+    FILETYPE_PWAD =    0x4,
+    FILETYPE_DEH =     0x8,
+};
+
+static int GuessFileType(const char *name)
+{
+    int ret = FILETYPE_UNKNOWN;
+    const char *base;
+    char *lower;
+    static boolean iwad_found = false;
+
+    base = M_BaseName(name);
+    lower = M_StringDuplicate(base);
+    M_ForceLowercase(lower);
+
+    // only ever add one argument to the -iwad parameter
+
+    if (iwad_found == false && D_IsIWADName(lower))
+    {
+        ret = FILETYPE_IWAD;
+        iwad_found = true;
+    }
+    else if (M_StringEndsWith(lower, ".wad") ||
+             M_StringEndsWith(lower, ".lmp"))
+    {
+        ret = FILETYPE_PWAD;
+    }
+    else if (M_StringEndsWith(lower, ".deh") ||
+//           M_StringEndsWith(lower, ".bex") ||
+             M_StringEndsWith(lower, ".hhe") ||
+             M_StringEndsWith(lower, ".seh"))
+    {
+        ret = FILETYPE_DEH;
+    }
+
+    free(lower);
+
+    return ret;
+}
+
+typedef struct
+{
+    char *str;
+    int type, stable;
+} argument_t;
+
+static int CompareByFileType(const void *a, const void *b)
+{
+    const argument_t *arg_a = (const argument_t *) a;
+    const argument_t *arg_b = (const argument_t *) b;
+
+    const int ret = arg_a->type - arg_b->type;
+
+    return ret ? ret : (arg_a->stable - arg_b->stable);
+}
+
+void M_AddLooseFiles(void)
+{
+    int i, types = 0;
+    char **newargv;
+    argument_t *arguments;
+
+    if (myargc < 2)
+    {
+        return;
+    }
+
+    // allocate space for up to three additional regular parameters
+
+    arguments = malloc((myargc + 3) * sizeof(*arguments));
+    memset(arguments, 0, (myargc + 3) * sizeof(*arguments));
+
+    // check the command line and make sure it does not already
+    // contain any regular parameters or response files
+    // but only fully-qualified LFS or UNC file paths
+
+    for (i = 1; i < myargc; i++)
+    {
+        char *arg = myargv[i];
+        int type;
+
+        if (strlen(arg) < 3 ||
+            arg[0] == '-' ||
+            arg[0] == '@' ||
+            ((!isalpha(arg[0]) || arg[1] != ':' || arg[2] != '\\') &&
+            (arg[0] != '\\' || arg[1] != '\\')))
+        {
+            free(arguments);
+            return;
+        }
+
+        type = GuessFileType(arg);
+        arguments[i].str = arg;
+        arguments[i].type = type;
+        arguments[i].stable = i;
+        types |= type;
+    }
+
+    // add space for one additional regular parameter
+    // for each discovered file type in the new argument  list
+    // and sort parameters right before their corresponding file paths
+
+    if (types & FILETYPE_IWAD)
+    {
+        arguments[myargc].str = M_StringDuplicate("-iwad");
+        arguments[myargc].type = FILETYPE_IWAD - 1;
+        myargc++;
+    }
+    if (types & FILETYPE_PWAD)
+    {
+        arguments[myargc].str = M_StringDuplicate("-merge");
+        arguments[myargc].type = FILETYPE_PWAD - 1;
+        myargc++;
+    }
+    if (types & FILETYPE_DEH)
+    {
+        arguments[myargc].str = M_StringDuplicate("-deh");
+        arguments[myargc].type = FILETYPE_DEH - 1;
+        myargc++;
+    }
+
+    newargv = malloc(myargc * sizeof(*newargv));
+
+    // sort the argument list by file type, except for the zeroth argument
+    // which is the executable invocation itself
+
+    SDL_qsort(arguments + 1, myargc - 1, sizeof(*arguments), CompareByFileType);
+
+    newargv[0] = myargv[0];
+
+    for (i = 1; i < myargc; i++)
+    {
+        newargv[i] = arguments[i].str;
+    }
+
+    free(arguments);
+
+    myargv = newargv;
+}
+#endif
 
 // Return the name of the executable used to start the program:
 
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -37,6 +37,7 @@
 int M_CheckParmWithArgs(const char *check, int num_args);
 
 void M_FindResponseFile(void);
+void M_AddLooseFiles(void);
 
 // Parameter has been specified?