1 /** @file virtualmappings.cpp  Maps WAD lumps and native files to virtual FS1 files.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2006-2007 Jamie Jones <jamie_jones_au@yahoo.com.au>
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #include "doomsday/filesys/fs_main.h"
23 #include "doomsday/filesys/fs_util.h"
24 #include "doomsday/filesys/lumpindex.h"
25 #include "doomsday/filesys/virtualmappings.h"
26 #include "doomsday/doomsdayapp.h"
27 
28 #include <de/memory.h>
29 #include <de/c_wrapper.h>
30 
31 #ifdef WIN32
32 #  define strupr _strupr
33 #endif
34 
35 using namespace de;
36 
FS_InitVirtualPathMappings()37 void FS_InitVirtualPathMappings()
38 {
39     App_FileSystem().clearPathMappings();
40 
41     if (DoomsdayApp::app().isShuttingDown()) return;
42 
43     // Create virtual directory mappings by processing all -vdmap options.
44     dint argC = CommandLine_Count();
45     for (dint i = 0; i < argC; ++i)
46     {
47         if (qstrnicmp("-vdmap", CommandLine_At(i), 6))
48         {
49             continue;
50         }
51 
52         if (i < argC - 1 && !CommandLine_IsOption(i + 1) && !CommandLine_IsOption(i + 2))
53         {
54             String source      = NativePath(CommandLine_PathAt(i + 1)).expand().withSeparators('/');
55             String destination = NativePath(CommandLine_PathAt(i + 2)).expand().withSeparators('/');
56             App_FileSystem().addPathMapping(source, destination);
57             i += 2;
58         }
59     }
60 }
61 
62 /// Skip all whitespace except newlines.
skipSpace(char const * ptr)63 static inline char const *skipSpace(char const *ptr)
64 {
65     DENG2_ASSERT(ptr != 0);
66     while (*ptr && *ptr != '\n' && isspace(*ptr))
67     { ptr++; }
68     return ptr;
69 }
70 
parsePathLumpMapping(char lumpName[9],ddstring_t * path,char const * buffer)71 static bool parsePathLumpMapping(char lumpName[9/*LUMPNAME_T_MAXLEN*/], ddstring_t *path, char const *buffer)
72 {
73     DENG2_ASSERT(lumpName != 0 && path != 0);
74 
75     // Find the start of the lump name.
76     char const *ptr = skipSpace(buffer);
77 
78     // Just whitespace?
79     if (!*ptr || *ptr == '\n') return false;
80 
81     // Find the end of the lump name.
82     char const *end = (char const *)M_FindWhite((char *)ptr);
83     if (!*end || *end == '\n') return false;
84 
85     size_t len = end - ptr;
86     // Invalid lump name?
87     if (len > 8) return false;
88 
89     memset(lumpName, 0, 9/*LUMPNAME_T_MAXLEN*/);
90     strncpy(lumpName, ptr, len);
91     strupr(lumpName);
92 
93     // Find the start of the file path.
94     ptr = skipSpace(end);
95     if (!*ptr || *ptr == '\n') return false; // Missing file path.
96 
97     // We're at the file path.
98     Str_Set(path, ptr);
99     // Get rid of any extra whitespace on the end.
100     Str_StripRight(path);
101     F_FixSlashes(path, path);
102     return true;
103 }
104 
105 /**
106  * <pre> LUMPNAM0 \\Path\\In\\The\\Base.ext
107  * LUMPNAM1 Path\\In\\The\\RuntimeDir.ext
108  *  :</pre>
109  */
parsePathLumpMappings(char const * buffer)110 static bool parsePathLumpMappings(char const *buffer)
111 {
112     DENG2_ASSERT(buffer != 0);
113 
114     bool successful = false;
115     ddstring_t path; Str_Init(&path);
116     ddstring_t line; Str_Init(&line);
117 
118     char const *ch = buffer;
119     char lumpName[9/*LUMPNAME_T_MAXLEN*/];
120     do
121     {
122         ch = Str_GetLine(&line, ch);
123         if (!parsePathLumpMapping(lumpName, &path, Str_Text(&line)))
124         {
125             // Failure parsing the mapping.
126             // Ignore errors in individual mappings and continue parsing.
127             //goto parseEnded;
128         }
129         else
130         {
131             String destination = NativePath(Str_Text(&path)).expand().withSeparators('/');
132             App_FileSystem().addPathLumpMapping(lumpName, destination);
133         }
134     } while (*ch);
135 
136     // Success.
137     successful = true;
138 
139 //parseEnded:
140     Str_Free(&line);
141     Str_Free(&path);
142     return successful;
143 }
144 
FS_InitPathLumpMappings()145 void FS_InitPathLumpMappings()
146 {
147     // Free old paths, if any.
148     App_FileSystem().clearPathLumpMappings();
149 
150     if (DoomsdayApp::app().isShuttingDown()) return;
151 
152     size_t bufSize = 0;
153     uint8_t *buf = 0;
154 
155     // Add the contents of all DD_DIREC lumps.
156     /// @todo fixme: Enforce scope to the containing package!
157     LumpIndex const &lumpIndex = App_FileSystem().nameIndex();
158     LumpIndex::FoundIndices foundDirecs;
159     lumpIndex.findAll("DD_DIREC.lmp", foundDirecs);
160     DENG2_FOR_EACH_CONST(LumpIndex::FoundIndices, i, foundDirecs) // in load order
161     {
162         File1 &lump = lumpIndex[*i];
163         FileInfo const &lumpInfo = lump.info();
164 
165         // Make a copy of it so we can ensure it ends in a null.
166         if (!buf || bufSize < lumpInfo.size + 1)
167         {
168             bufSize = lumpInfo.size + 1;
169             buf = (uint8_t *) M_Realloc(buf, bufSize);
170         }
171 
172         lump.read(buf, 0, lumpInfo.size);
173         buf[lumpInfo.size] = 0;
174         parsePathLumpMappings(reinterpret_cast<char const *>(buf));
175     }
176 
177     M_Free(buf);
178 }
179