1 /*
2 ** p_tags.cpp
3 ** everything to do with tags and their management
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2015 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 **
34 */
35 
36 
37 #include "p_tags.h"
38 #include "c_dispatch.h"
39 
40 FTagManager tagManager;
41 
42 //-----------------------------------------------------------------------------
43 //
44 //
45 //
46 //-----------------------------------------------------------------------------
47 
sectindex(const sector_t * sector)48 static inline int sectindex(const sector_t *sector)
49 {
50 	return (int)(intptr_t)(sector - sectors);
51 }
52 
lineindex(const line_t * line)53 static inline int lineindex(const line_t *line)
54 {
55 	return (int)(intptr_t)(line - lines);
56 }
57 
58 //-----------------------------------------------------------------------------
59 //
60 //
61 //
62 //-----------------------------------------------------------------------------
63 
AddSectorTag(int sector,int tag)64 void FTagManager::AddSectorTag(int sector, int tag)
65 {
66 	if (tag == 0) return;
67 
68 	// This function assumes that all tags for a single sector get added sequentially.
69 	// Should there ever be some need for compatibility.txt to add tags to sectors which already have a tag this function needs to be changed to adjust the startForSector indices.
70 	while (startForSector.Size() <= (unsigned int)sector)
71 	{
72 		startForSector.Push(-1);
73 	}
74 	if (startForSector[sector] == -1)
75 	{
76 		startForSector[sector] = allTags.Size();
77 	}
78 	else
79 	{
80 		// check if the key was already defined
81 		for (unsigned i = startForSector[sector]; i < allTags.Size(); i++)
82 		{
83 			if (allTags[i].tag == tag)
84 			{
85 				return;
86 			}
87 		}
88 	}
89 	FTagItem it = { sector, tag, -1 };
90 	allTags.Push(it);
91 }
92 
93 //-----------------------------------------------------------------------------
94 //
95 //
96 //
97 //-----------------------------------------------------------------------------
98 
RemoveSectorTags(int sect)99 void FTagManager::RemoveSectorTags(int sect)
100 {
101 	if (startForSector.Size() > (unsigned int)sect)
102 	{
103 		int start = startForSector[sect];
104 		if (start >= 0)
105 		{
106 			while (allTags[start].target == sect)
107 			{
108 				allTags[start].tag = allTags[start].target = -1;
109 				start++;
110 			}
111 		}
112 	}
113 }
114 
115 //-----------------------------------------------------------------------------
116 //
117 //
118 //
119 //-----------------------------------------------------------------------------
120 
AddLineID(int line,int tag)121 void FTagManager::AddLineID(int line, int tag)
122 {
123 	if (tag == -1) return;	// For line IDs -1 means 'not set', unlike sectors.
124 
125 	// This function assumes that all ids for a single line get added sequentially.
126 	while (startForLine.Size() <= (unsigned int)line)
127 	{
128 		startForLine.Push(-1);
129 	}
130 	if (startForLine[line] == -1)
131 	{
132 		startForLine[line] = allIDs.Size();
133 	}
134 	else
135 	{
136 		// check if the key was already defined
137 		for (unsigned i = startForLine[line]; i < allIDs.Size(); i++)
138 		{
139 			if (allIDs[i].tag == tag)
140 			{
141 				return;
142 			}
143 		}
144 	}
145 	FTagItem it = { line, tag, -1 };
146 	allIDs.Push(it);
147 }
148 
149 //-----------------------------------------------------------------------------
150 //
151 //
152 //
153 //-----------------------------------------------------------------------------
154 
HashTags()155 void FTagManager::HashTags()
156 {
157 	// add an end marker so we do not need to check for the array's size in the other functions.
158 	static FTagItem it = { -1, -1, -1 };
159 	allTags.Push(it);
160 	allIDs.Push(it);
161 
162 	// Initially make all slots empty.
163 	memset(TagHashFirst, -1, sizeof(TagHashFirst));
164 	memset(IDHashFirst, -1, sizeof(IDHashFirst));
165 
166 	// Proceed from last to first so that lower targets appear first
167 	for (int i = allTags.Size() - 1; i >= 0; i--)
168 	{
169 		if (allTags[i].target >= 0)	// only link valid entries
170 		{
171 			int hash = ((unsigned int)allTags[i].tag) % FTagManager::TAG_HASH_SIZE;
172 			allTags[i].nexttag = TagHashFirst[hash];
173 			TagHashFirst[hash] = i;
174 		}
175 	}
176 
177 	for (int i = allIDs.Size() - 1; i >= 0; i--)
178 	{
179 		if (allIDs[i].target >= 0)	// only link valid entries
180 		{
181 			int hash = ((unsigned int)allIDs[i].tag) % FTagManager::TAG_HASH_SIZE;
182 			allIDs[i].nexttag = IDHashFirst[hash];
183 			IDHashFirst[hash] = i;
184 		}
185 	}
186 
187 }
188 
189 //-----------------------------------------------------------------------------
190 //
191 //
192 //
193 //-----------------------------------------------------------------------------
194 
SectorHasTags(const sector_t * sector) const195 bool FTagManager::SectorHasTags(const sector_t *sector) const
196 {
197 	int i = sectindex(sector);
198 	return SectorHasTags(i);
199 }
200 
201 //-----------------------------------------------------------------------------
202 //
203 //
204 //
205 //-----------------------------------------------------------------------------
206 
GetFirstSectorTag(const sector_t * sect) const207 int FTagManager::GetFirstSectorTag(const sector_t *sect) const
208 {
209 	int i = sectindex(sect);
210 	return SectorHasTags(i) ? allTags[startForSector[i]].tag : 0;
211 }
212 
213 //-----------------------------------------------------------------------------
214 //
215 //
216 //
217 //-----------------------------------------------------------------------------
218 
SectorHasTag(int i,int tag) const219 bool FTagManager::SectorHasTag(int i, int tag) const
220 {
221 	if (SectorHasTags(i))
222 	{
223 		int ndx = startForSector[i];
224 		while (allTags[ndx].target == i)
225 		{
226 			if (allTags[ndx].tag == tag) return true;
227 			ndx++;
228 		}
229 	}
230 	return false;
231 }
232 
233 //-----------------------------------------------------------------------------
234 //
235 //
236 //
237 //-----------------------------------------------------------------------------
238 
SectorHasTag(const sector_t * sector,int tag) const239 bool FTagManager::SectorHasTag(const sector_t *sector, int tag) const
240 {
241 	return SectorHasTag(sectindex(sector), tag);
242 }
243 
244 //-----------------------------------------------------------------------------
245 //
246 //
247 //
248 //-----------------------------------------------------------------------------
249 
LineHasID(int i,int tag) const250 bool FTagManager::LineHasID(int i, int tag) const
251 {
252 	if (LineHasIDs(i))
253 	{
254 		int ndx = startForLine[i];
255 		while (allIDs[ndx].target == i)
256 		{
257 			if (allIDs[ndx].tag == tag) return true;
258 			ndx++;
259 		}
260 	}
261 	return false;
262 }
263 
264 //-----------------------------------------------------------------------------
265 //
266 //
267 //
268 //-----------------------------------------------------------------------------
269 
LineHasID(const line_t * line,int tag) const270 bool FTagManager::LineHasID(const line_t *line, int tag) const
271 {
272 	return LineHasID(lineindex(line), tag);
273 }
274 
275 //-----------------------------------------------------------------------------
276 //
277 //
278 //
279 //-----------------------------------------------------------------------------
280 
DumpTags()281 void FTagManager::DumpTags()
282 {
283 	for (unsigned i = 0; i < allTags.Size(); i++)
284 	{
285 		Printf("Sector %d, tag %d\n", allTags[i].target, allTags[i].tag);
286 	}
287 	for (unsigned i = 0; i < allIDs.Size(); i++)
288 	{
289 		Printf("Line %d, ID %d\n", allIDs[i].target, allIDs[i].tag);
290 	}
291 }
292 
CCMD(dumptags)293 CCMD(dumptags)
294 {
295 	tagManager.DumpTags();
296 }
297 
298 //-----------------------------------------------------------------------------
299 //
300 // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
301 //
302 // Find the next sector with a specified tag.
303 // Rewritten by Lee Killough to use chained hashing to improve speed
304 //
305 //-----------------------------------------------------------------------------
306 
Next()307 int FSectorTagIterator::Next()
308 {
309 	int ret;
310 	if (searchtag == INT_MIN)
311 	{
312 		ret = start;
313 		start = -1;
314 	}
315 	else if (searchtag != 0)
316 	{
317 		while (start >= 0 && tagManager.allTags[start].tag != searchtag) start = tagManager.allTags[start].nexttag;
318 		if (start == -1) return -1;
319 		ret = tagManager.allTags[start].target;
320 		start = tagManager.allTags[start].nexttag;
321 	}
322 	else
323 	{
324 		// with the tag manager, searching for tag 0 has to be different, because it won't create entries for untagged sectors.
325 		while (start < numsectors && tagManager.SectorHasTags(start))
326 		{
327 			start++;
328 		}
329 		if (start == numsectors) return -1;
330 		ret = start;
331 		start++;
332 	}
333 	return ret;
334 }
335 
336 //-----------------------------------------------------------------------------
337 //
338 // linear search for compatible stair building
339 //
340 //-----------------------------------------------------------------------------
341 
NextCompat(bool compat,int start)342 int FSectorTagIterator::NextCompat(bool compat, int start)
343 {
344 	if (!compat) return Next();
345 
346 	for (int i = start + 1; i < numsectors; i++)
347 	{
348 		if (tagManager.SectorHasTag(i, searchtag)) return i;
349 	}
350 	return -1;
351 }
352 
353 
354 //-----------------------------------------------------------------------------
355 //
356 // killough 4/16/98: Same thing, only for linedefs
357 //
358 //-----------------------------------------------------------------------------
359 
Next()360 int FLineIdIterator::Next()
361 {
362 	while (start >= 0 && tagManager.allIDs[start].tag != searchtag) start = tagManager.allIDs[start].nexttag;
363 	if (start == -1) return -1;
364 	int ret = tagManager.allIDs[start].target;
365 	start = tagManager.allIDs[start].nexttag;
366 	return ret;
367 }
368