1 /** @file lumpindex.h  Index of lumps.
2  *
3  * @todo Move the definition of lumpnum_t into this header.
4  *
5  * @author Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
6  * @author Copyright © 2006-2014 Daniel Swanson <danij@dengine.net>
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA</small>
21  */
22 
23 #ifndef LIBDENG_FILESYS_LUMPINDEX_H
24 #define LIBDENG_FILESYS_LUMPINDEX_H
25 
26 #ifdef __cplusplus
27 
28 #include "../libdoomsday.h"
29 #include "file.h"
30 #include "fileinfo.h"
31 
32 #include <QList>
33 #include <de/Error>
34 
35 namespace de {
36 
37 /**
38  * Virtual file system component used to model an indexable collection of
39  * lumps. A single index may include lumps originating from many different
40  * file containers.
41  *
42  * @ingroup fs
43  */
44 class LIBDOOMSDAY_PUBLIC LumpIndex
45 {
46 public:
47     /// No file(s) found. @ingroup errors
48     DENG2_ERROR(NotFoundError);
49 
50     typedef QList<File1 *> Lumps;
51     typedef std::list<lumpnum_t> FoundIndices;
52 
53     /**
54      * Heuristic based map data (format) recognizer.
55      *
56      * Unfortunately id Tech 1 maps cannot be easily recognized, due to their
57      * lack of identification signature, the mechanics of the WAD format lump
58      * index and the existence of several subformat variations. Therefore it is
59      * necessary to use heuristic analysis of the lump index and the lump data.
60      */
61     class LIBDOOMSDAY_PUBLIC Id1MapRecognizer
62     {
63     public:
64         /// Logical map format identifiers.
65         enum Format {
66             UnknownFormat = -1,
67 
68             DoomFormat,
69             HexenFormat,
70             Doom64Format,
71             UniversalFormat, // UDMF
72 
73             KnownFormatCount
74         };
75 
76         /// Logical map data type identifiers:
77         enum DataType {
78             UnknownData = -1,
79 
80             ThingData,
81             LineDefData,
82             SideDefData,
83             VertexData,
84             SegData,
85             SubsectorData,
86             NodeData,
87             SectorDefData,
88             RejectData,
89             BlockmapData,
90             BehaviorData,
91             ScriptData,
92             TintColorData,
93             MacroData,
94             LeafData,
95             GLVertexData,
96             GLSegData,
97             GLSubsectorData,
98             GLNodeData,
99             GLPVSData,
100             UDMFTextmapData,
101             UDMFEndmapData,
102 
103             KnownDataCount
104         };
105 
106         typedef QMap<DataType, File1 *> Lumps;
107 
108     public:
109         /**
110          * Attempt to recognize an id Tech 1 format by traversing the WAD lump
111          * index, beginning at the @a lumpIndexOffset specified.
112          */
113         Id1MapRecognizer(LumpIndex const &lumpIndex, lumpnum_t lumpIndexOffset);
114 
115         String const &id() const;
116         Format format() const;
117 
118         Lumps const &lumps() const;
119 
120         File1 *sourceFile() const;
121 
122         /**
123          * Returns the lump index number of the last data lump inspected by the
124          * recognizer, making it possible to collate/locate all the map data sets
125          * using multiple recognizers.
126          */
127         lumpnum_t lastLump() const;
128 
129     public:
130         /**
131          * Returns the textual name for the identified map format @a id.
132          */
133         static String const &formatName(Format id);
134 
135         /**
136          * Determines the type of a map data lump by @a name.
137          */
138         static DataType typeForLumpName(String name);
139 
140         /**
141          * Determine the size (in bytes) of an element of the specified map data
142          * lump @a type for the current map format.
143          *
144          * @param mapFormat     Map format identifier.
145          * @param dataType      Map lump data type.
146          *
147          * @return Size of an element of the specified type.
148          */
149         static dsize elementSizeForDataType(Format mapFormat, DataType dataType);
150 
151     private:
152         DENG2_PRIVATE(d)
153     };
154 
155 public:
156     /**
157      * @param pathsAreUnique  Lumps in the index must have unique paths. Inserting
158      *                        a lump with the same path as one which already exists
159      *                        will result in the earlier lump being pruned.
160      */
161     explicit LumpIndex(bool pathsAreUnique = false);
162     virtual ~LumpIndex();
163 
164     /**
165      * Returns @c true iff the directory contains no lumps.
166      */
isEmpty()167     inline bool isEmpty() const { return !size(); }
168 
169     /**
170      * Returns the total number of lumps in the directory.
171      */
172     int size() const;
173 
174     /// @copydoc size()
lumpCount()175     inline int lumpCount() const { return size(); } // alias
176 
177     /**
178      * Returns the logicial index of the last lump in the directory, or @c -1 if empty.
179      */
180     int lastIndex() const;
181 
182     /**
183      * Returns @c true iff @a lumpNum can be interpreted as a valid lump index.
184      */
185     bool hasLump(lumpnum_t lumpNum) const;
186 
187     /**
188      * Returns @c true iff the index contains one or more lumps with a matching @a path.
189      */
190     bool contains(Path const &path) const;
191 
192     /**
193      * Finds all indices for lumps with a matching @a path.
194      *
195      * @param path   Path of the lump(s) to .
196      * @param found  Set of lumps that match @a path (in load order; most recent last).
197      *
198      * @return  Number of lumps found.
199      *
200      * @see findFirst(), findLast()
201      */
202     int findAll(Path const &path, FoundIndices &found) const;
203 
204     /**
205      * Returns the index of the @em first loaded lump with a matching @a path.
206      * If no lump is found then @c -1 is returned.
207      *
208      * @see findLast(), findAll()
209      */
210     lumpnum_t findFirst(Path const &path) const;
211 
212     /**
213      * Returns the index of the @em last loaded lump with a matching @a path.
214      * If no lump is found then @c -1 is returned.
215      *
216      * @see findFirst(), findAll()
217      */
218     lumpnum_t findLast(Path const &path) const;
219 
220     /**
221      * Lookup a file at specific offset in the index.
222      *
223      * @param lumpNum  Logical lumpnum associated to the file being looked up.
224      *
225      * @return  The requested file.
226      *
227      * @throws NotFoundError If the requested file could not be found.
228      * @see hasLump()
229      */
230     File1 &lump(lumpnum_t lumpNum) const;
231 
232     /**
233      * @copydoc lump()
234      * @see lump()
235      */
236     inline File1 &operator [] (lumpnum_t lumpNum) const { return lump(lumpNum); }
237 
238     /**
239      * Provides access to list containing @em all the lumps, for efficient traversals.
240      */
241     Lumps const &allLumps() const;
242 
243     /**
244      * Clear the index back to its default (i.e., empty state).
245      */
246     void clear();
247 
248     /**
249      * Are any lumps from @a file published in this index?
250      *
251      * @param file  File containing the lumps to look for.
252      *
253      * @return  @c true= One or more lumps are included.
254      */
255     bool catalogues(File1 &file);
256 
257     /**
258      * Append a lump to the index.
259      *
260      * @post Lump name hashes may be invalidated (will be rebuilt upon next search).
261      *
262      * @param lump  Lump to be being added.
263      */
264     void catalogLump(File1 &lump);
265 
266     /**
267      * Prune all lumps catalogued from @a file.
268      *
269      * @param file  File containing the lumps to prune
270      *
271      * @return  Number of lumps pruned.
272      */
273     int pruneByFile(File1 &file);
274 
275     /**
276      * Prune the lump referenced by @a lumpInfo.
277      *
278      * @param lump  Lump file to prune.
279      *
280      * @return  @c true if found and pruned.
281      */
282     bool pruneLump(File1 &lump);
283 
284 public:
285     /**
286      * Compose the path to the data resource.
287      *
288      * @param lumpNum  Lump number.
289      *
290      * @note We do not use the lump name, instead we use the logical lump index
291      * in the global LumpIndex. This is necessary because of the way id tech 1
292      * manages graphic references in animations (intermediate frames are chosen
293      * by their 'original indices' rather than by name).
294      */
295     static de::Uri composeResourceUrn(lumpnum_t lumpNum);
296 
297 private:
298     DENG2_PRIVATE(d)
299 };
300 
301 typedef LumpIndex::Id1MapRecognizer Id1MapRecognizer;
302 
303 } // namespace de
304 
305 #endif // __cplusplus
306 #endif // LIBDENG_FILESYS_LUMPINDEX_H
307