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