1 /**
2  * @file engine.cpp
3  *
4  * Implementation of basic engine helper functions:
5  * - Sprite blitting
6  * - Drawing
7  * - Angle calculation
8  * - RNG
9  * - Memory allocation
10  * - File loading
11  * - Video playback
12  */
13 #include "all.h"
14 #include "options.h"
15 #include "../3rdParty/Storm/Source/storm.h"
16 
17 DEVILUTION_BEGIN_NAMESPACE
18 
19 /** Seed value before the most recent call to SetRndSeed() */
20 Sint32 orgseed;
21 /** Current game seed */
22 Sint32 sglGameSeed;
23 static CCritSect sgMemCrit;
24 
25 /**
26  * Specifies the increment used in the Borland C/C++ pseudo-random.
27  */
28 const Uint32 RndInc = 1;
29 
30 /**
31  * Specifies the multiplier used in the Borland C/C++ pseudo-random number generator algorithm.
32  */
33 const Uint32 RndMult = 0x015A4E35;
34 
CelDrawTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)35 void CelDrawTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
36 {
37 	int nDataSize;
38 	BYTE *pRLEBytes;
39 
40 	assert(pCelBuff != NULL);
41 	pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
42 	CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
43 }
44 
CelClippedDrawTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)45 void CelClippedDrawTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
46 {
47 	BYTE *pRLEBytes;
48 	int nDataSize;
49 
50 	assert(pCelBuff != NULL);
51 
52 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
53 
54 	CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
55 }
56 
CelDrawLightTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth,BYTE * tbl)57 void CelDrawLightTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, BYTE *tbl)
58 {
59 	int nDataSize;
60 	BYTE *pRLEBytes;
61 
62 	assert(pCelBuff != NULL);
63 
64 	pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
65 
66 	if (light_table_index || tbl)
67 		CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth, tbl);
68 	else
69 		CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
70 }
71 
CelClippedDrawLightTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)72 void CelClippedDrawLightTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
73 {
74 	int nDataSize;
75 	BYTE *pRLEBytes;
76 
77 	assert(pCelBuff != NULL);
78 
79 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
80 
81 	if (light_table_index)
82 		CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth, NULL);
83 	else
84 		CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
85 }
86 
CelDrawLightRedTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth,char light)87 void CelDrawLightRedTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light)
88 {
89 	int nDataSize, w, idx;
90 	BYTE *pRLEBytes, *dst, *tbl;
91 
92 	assert(pCelBuff != NULL);
93 
94 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
95 	dst = out.at(sx, sy);
96 
97 	idx = light4flag ? 1024 : 4096;
98 	if (light == 2)
99 		idx += 256; // gray colors
100 	if (light >= 4)
101 		idx += (light - 1) << 8;
102 
103 	BYTE width;
104 	BYTE *end;
105 
106 	tbl = &pLightTbl[idx];
107 	end = &pRLEBytes[nDataSize];
108 
109 	for (; pRLEBytes != end; dst -= out.pitch() + nWidth) {
110 		for (w = nWidth; w;) {
111 			width = *pRLEBytes++;
112 			if (!(width & 0x80)) {
113 				w -= width;
114 				while (width) {
115 					*dst = tbl[*pRLEBytes];
116 					pRLEBytes++;
117 					dst++;
118 					width--;
119 				}
120 			} else {
121 				width = -(char)width;
122 				dst += width;
123 				w -= width;
124 			}
125 		}
126 	}
127 }
128 
CelBlitSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth)129 void CelBlitSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth)
130 {
131 	int i, w;
132 	BYTE width;
133 	BYTE *src, *dst;
134 
135 	assert(pRLEBytes != NULL);
136 
137 	src = pRLEBytes;
138 	dst = out.at(sx, sy);
139 	w = nWidth;
140 
141 	for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + w) {
142 		for (i = w; i;) {
143 			width = *src++;
144 			if (!(width & 0x80)) {
145 				i -= width;
146 				if (dst < out.end() && dst > out.begin()) {
147 					memcpy(dst, src, width);
148 				}
149 				src += width;
150 				dst += width;
151 			} else {
152 				width = -(char)width;
153 				dst += width;
154 				i -= width;
155 			}
156 		}
157 	}
158 }
159 
CelClippedDrawSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)160 void CelClippedDrawSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
161 {
162 	BYTE *pRLEBytes;
163 	int nDataSize;
164 
165 	assert(pCelBuff != NULL);
166 
167 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
168 	CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
169 }
170 
CelBlitLightSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth,BYTE * tbl)171 void CelBlitLightSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *tbl)
172 {
173 	int i, w;
174 	BYTE width;
175 	BYTE *src, *dst;
176 
177 	assert(pRLEBytes != NULL);
178 
179 	src = pRLEBytes;
180 	dst = out.at(sx, sy);
181 	if (tbl == NULL)
182 		tbl = &pLightTbl[light_table_index * 256];
183 	w = nWidth;
184 
185 	for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + w) {
186 		for (i = w; i;) {
187 			width = *src++;
188 			if (!(width & 0x80)) {
189 				i -= width;
190 				if (dst < out.end() && dst > out.begin()) {
191 					if (width & 1) {
192 						dst[0] = tbl[src[0]];
193 						src++;
194 						dst++;
195 					}
196 					width >>= 1;
197 					if (width & 1) {
198 						dst[0] = tbl[src[0]];
199 						dst[1] = tbl[src[1]];
200 						src += 2;
201 						dst += 2;
202 					}
203 					width >>= 1;
204 					for (; width; width--) {
205 						dst[0] = tbl[src[0]];
206 						dst[1] = tbl[src[1]];
207 						dst[2] = tbl[src[2]];
208 						dst[3] = tbl[src[3]];
209 						src += 4;
210 						dst += 4;
211 					}
212 				} else {
213 					src += width;
214 					dst += width;
215 				}
216 			} else {
217 				width = -(char)width;
218 				dst += width;
219 				i -= width;
220 			}
221 		}
222 	}
223 }
224 
CelBlitLightTransSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth)225 void CelBlitLightTransSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth)
226 {
227 	int w;
228 	BOOL shift;
229 	BYTE *tbl;
230 
231 	assert(pRLEBytes != NULL);
232 	int i;
233 	BYTE width;
234 	BYTE *src, *dst;
235 
236 	src = pRLEBytes;
237 	dst = out.at(sx, sy);
238 	tbl = &pLightTbl[light_table_index * 256];
239 	w = nWidth;
240 	shift = (BYTE)(size_t)dst & 1;
241 
242 	for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + w, shift = (shift + 1) & 1) {
243 		for (i = w; i;) {
244 			width = *src++;
245 			if (!(width & 0x80)) {
246 				i -= width;
247 				if (dst < out.end() && dst > out.begin()) {
248 					if (((BYTE)(size_t)dst & 1) == shift) {
249 						if (!(width & 1)) {
250 							goto L_ODD;
251 						} else {
252 							src++;
253 							dst++;
254 						L_EVEN:
255 							width >>= 1;
256 							if (width & 1) {
257 								dst[0] = tbl[src[0]];
258 								src += 2;
259 								dst += 2;
260 							}
261 							width >>= 1;
262 							for (; width; width--) {
263 								dst[0] = tbl[src[0]];
264 								dst[2] = tbl[src[2]];
265 								src += 4;
266 								dst += 4;
267 							}
268 						}
269 					} else {
270 						if (!(width & 1)) {
271 							goto L_EVEN;
272 						} else {
273 							dst[0] = tbl[src[0]];
274 							src++;
275 							dst++;
276 						L_ODD:
277 							width >>= 1;
278 							if (width & 1) {
279 								dst[1] = tbl[src[1]];
280 								src += 2;
281 								dst += 2;
282 							}
283 							width >>= 1;
284 							for (; width; width--) {
285 								dst[1] = tbl[src[1]];
286 								dst[3] = tbl[src[3]];
287 								src += 4;
288 								dst += 4;
289 							}
290 						}
291 					}
292 				} else {
293 					src += width;
294 					dst += width;
295 				}
296 			} else {
297 				width = -(char)width;
298 				dst += width;
299 				i -= width;
300 			}
301 		}
302 	}
303 }
304 
305 /**
306  * @brief Same as CelBlitLightSafe, with blended transparancy applied
307  * @param out The output buffer
308  * @param pRLEBytes CEL pixel stream (run-length encoded)
309  * @param nDataSize Size of CEL in bytes
310  * @param nWidth Width of sprite
311  * @param tbl Palette translation table
312  */
CelBlitLightBlendedSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth,BYTE * tbl)313 static void CelBlitLightBlendedSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *tbl)
314 {
315 	int i, w;
316 	BYTE width;
317 	BYTE *src, *dst;
318 
319 	assert(pRLEBytes != NULL);
320 
321 	src = pRLEBytes;
322 	dst = out.at(sx, sy);
323 	if (tbl == NULL)
324 		tbl = &pLightTbl[light_table_index * 256];
325 	w = nWidth;
326 
327 	for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + w) {
328 		for (i = w; i;) {
329 			width = *src++;
330 			if (!(width & 0x80)) {
331 				i -= width;
332 				if (dst < out.end() && dst > out.begin()) {
333 					if (width & 1) {
334 						dst[0] = paletteTransparencyLookup[dst[0]][tbl[src[0]]];
335 						src++;
336 						dst++;
337 					}
338 					width >>= 1;
339 					if (width & 1) {
340 						dst[0] = paletteTransparencyLookup[dst[0]][tbl[src[0]]];
341 						dst[1] = paletteTransparencyLookup[dst[1]][tbl[src[1]]];
342 						src += 2;
343 						dst += 2;
344 					}
345 					width >>= 1;
346 					for (; width; width--) {
347 						dst[0] = paletteTransparencyLookup[dst[0]][tbl[src[0]]];
348 						dst[1] = paletteTransparencyLookup[dst[1]][tbl[src[1]]];
349 						dst[2] = paletteTransparencyLookup[dst[2]][tbl[src[2]]];
350 						dst[3] = paletteTransparencyLookup[dst[3]][tbl[src[3]]];
351 						src += 4;
352 						dst += 4;
353 					}
354 				} else {
355 					src += width;
356 					dst += width;
357 				}
358 			} else {
359 				width = -(char)width;
360 				dst += width;
361 				i -= width;
362 			}
363 		}
364 	}
365 }
366 
CelClippedBlitLightTransTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)367 void CelClippedBlitLightTransTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
368 {
369 	int nDataSize;
370 	BYTE *pRLEBytes;
371 
372 	assert(pCelBuff != NULL);
373 
374 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
375 
376 	if (cel_transparency_active) {
377 		if (sgOptions.Graphics.bBlendedTransparancy)
378 			CelBlitLightBlendedSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth, NULL);
379 		else
380 			CelBlitLightTransSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
381 	} else if (light_table_index)
382 		CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth, NULL);
383 	else
384 		CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, nWidth);
385 }
386 
CelDrawLightRedSafeTo(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth,char light)387 void CelDrawLightRedSafeTo(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light)
388 {
389 	int nDataSize, w, idx;
390 	BYTE *pRLEBytes, *dst, *tbl;
391 
392 	assert(pCelBuff != NULL);
393 
394 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
395 	dst = out.at(sx, sy);
396 
397 	idx = light4flag ? 1024 : 4096;
398 	if (light == 2)
399 		idx += 256; // gray colors
400 	if (light >= 4)
401 		idx += (light - 1) << 8;
402 
403 	tbl = &pLightTbl[idx];
404 
405 	BYTE width;
406 	BYTE *end;
407 
408 	end = &pRLEBytes[nDataSize];
409 
410 	for (; pRLEBytes != end; dst -= out.pitch() + nWidth) {
411 		for (w = nWidth; w;) {
412 			width = *pRLEBytes++;
413 			if (!(width & 0x80)) {
414 				w -= width;
415 				if (dst < out.end() && dst > out.begin()) {
416 					while (width) {
417 						*dst = tbl[*pRLEBytes];
418 						pRLEBytes++;
419 						dst++;
420 						width--;
421 					}
422 				} else {
423 					pRLEBytes += width;
424 					dst += width;
425 				}
426 			} else {
427 				width = -(char)width;
428 				dst += width;
429 				w -= width;
430 			}
431 		}
432 	}
433 }
434 
CelDrawUnsafeTo(CelOutputBuffer out,int x,int y,BYTE * pCelBuff,int nCel,int nWidth)435 void CelDrawUnsafeTo(CelOutputBuffer out, int x, int y, BYTE *pCelBuff, int nCel, int nWidth)
436 {
437 	BYTE *pRLEBytes, *dst, *end;
438 
439 	assert(pCelBuff != NULL);
440 	int i, nDataSize;
441 	BYTE width;
442 
443 	pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
444 	end = &pRLEBytes[nDataSize];
445 	dst = out.at(x, y);
446 
447 	for (; pRLEBytes != end; dst -= out.pitch() + nWidth) {
448 		for (i = nWidth; i;) {
449 			width = *pRLEBytes++;
450 			if (!(width & 0x80)) {
451 				i -= width;
452 				memcpy(dst, pRLEBytes, width);
453 				dst += width;
454 				pRLEBytes += width;
455 			} else {
456 				width = -(char)width;
457 				dst += width;
458 				i -= width;
459 			}
460 		}
461 	}
462 }
463 
CelBlitOutlineTo(CelOutputBuffer out,BYTE col,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth,bool skipColorIndexZero)464 void CelBlitOutlineTo(CelOutputBuffer out, BYTE col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, bool skipColorIndexZero)
465 {
466 	int nDataSize, w;
467 	BYTE *src, *dst, *end;
468 	BYTE width;
469 
470 	assert(pCelBuff != NULL);
471 	src = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
472 	end = &src[nDataSize];
473 	dst = out.at(sx, sy);
474 
475 	for (; src != end; dst -= out.pitch() + nWidth) {
476 		for (w = nWidth; w;) {
477 			width = *src++;
478 			if (!(width & 0x80)) {
479 				w -= width;
480 				if (dst < out.end() && dst > out.begin()) {
481 					if (dst >= out.end() - out.pitch()) {
482 						while (width) {
483 							if (!skipColorIndexZero || *src > 0) {
484 								dst[-out.pitch()] = col;
485 								dst[-1] = col;
486 								dst[1] = col;
487 							}
488 							src++;
489 							dst++;
490 							width--;
491 						}
492 					} else {
493 						while (width) {
494 							if (!skipColorIndexZero || *src > 0) {
495 								dst[-out.pitch()] = col;
496 								dst[-1] = col;
497 								dst[1] = col;
498 								dst[out.pitch()] = col;
499 							}
500 							src++;
501 							dst++;
502 							width--;
503 						}
504 					}
505 				} else {
506 					src += width;
507 					dst += width;
508 				}
509 			} else {
510 				width = -(char)width;
511 				dst += width;
512 				w -= width;
513 			}
514 		}
515 	}
516 }
517 
SetPixel(CelOutputBuffer out,int sx,int sy,BYTE col)518 void SetPixel(CelOutputBuffer out, int sx, int sy, BYTE col)
519 {
520 	if (!out.in_bounds(sx, sy))
521 		return;
522 	*out.at(sx, sy) = col;
523 }
524 
DrawLineTo(CelOutputBuffer out,int x0,int y0,int x1,int y1,BYTE color_index)525 void DrawLineTo(CelOutputBuffer out, int x0, int y0, int x1, int y1, BYTE color_index)
526 {
527 	int i, dx, dy, steps;
528 	float ix, iy, sx, sy;
529 
530 	dx = x1 - x0;
531 	dy = y1 - y0;
532 	steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);
533 	ix = dx / (float)steps;
534 	iy = dy / (float)steps;
535 	sx = x0;
536 	sy = y0;
537 
538 	for (i = 0; i <= steps; i++, sx += ix, sy += iy) {
539 		SetPixel(out, sx, sy, color_index);
540 	}
541 }
542 
DrawHalfTransparentBlendedRectTo(CelOutputBuffer out,int sx,int sy,int width,int height)543 static void DrawHalfTransparentBlendedRectTo(CelOutputBuffer out, int sx, int sy, int width, int height)
544 {
545 	BYTE *pix = out.at(sx, sy);
546 	for (int row = 0; row < height; row++) {
547 		for (int col = 0; col < width; col++) {
548 			*pix = paletteTransparencyLookup[0][*pix];
549 			pix++;
550 		}
551 		pix += out.pitch() - width;
552 	}
553 }
554 
DrawHalfTransparentStippledRectTo(CelOutputBuffer out,int sx,int sy,int width,int height)555 static void DrawHalfTransparentStippledRectTo(CelOutputBuffer out, int sx, int sy, int width, int height)
556 {
557 	BYTE *pix = out.at(sx, sy);
558 	for (int row = 0; row < height; row++) {
559 		for (int col = 0; col < width; col++) {
560 			if ((row & 1 && col & 1) || (!(row & 1) && !(col & 1)))
561 				*pix = 0;
562 			pix++;
563 		}
564 		pix += out.pitch() - width;
565 	}
566 }
567 
DrawHalfTransparentRectTo(CelOutputBuffer out,int sx,int sy,int width,int height)568 void DrawHalfTransparentRectTo(CelOutputBuffer out, int sx, int sy, int width, int height)
569 {
570 	if (sgOptions.Graphics.bBlendedTransparancy) {
571 		DrawHalfTransparentBlendedRectTo(out, sx, sy, width, height);
572 	} else {
573 		DrawHalfTransparentStippledRectTo(out, sx, sy, width, height);
574 	}
575 }
576 
GetDirection(int x1,int y1,int x2,int y2)577 direction GetDirection(int x1, int y1, int x2, int y2)
578 {
579 	int mx, my, ny;
580 	direction md;
581 
582 	mx = x2 - x1;
583 	my = y2 - y1;
584 
585 	if (mx >= 0) {
586 		if (my >= 0) {
587 			md = DIR_S;
588 			if (2 * mx < my)
589 				md = DIR_SW;
590 		} else {
591 			my = -my;
592 			md = DIR_E;
593 			if (2 * mx < my)
594 				md = DIR_NE;
595 		}
596 		if (2 * my < mx)
597 			return DIR_SE;
598 	} else {
599 		if (my >= 0) {
600 			ny = -mx;
601 			md = DIR_W;
602 			if (2 * ny < my)
603 				md = DIR_SW;
604 		} else {
605 			ny = -mx;
606 			my = -my;
607 			md = DIR_N;
608 			if (2 * ny < my)
609 				md = DIR_NE;
610 		}
611 		if (2 * my < ny)
612 			return DIR_NW;
613 	}
614 
615 	return md;
616 }
617 
618 /**
619  * @brief Set the RNG seed
620  * @param s RNG seed
621  */
SetRndSeed(Sint32 s)622 void SetRndSeed(Sint32 s)
623 {
624 	sglGameSeed = s;
625 	orgseed = s;
626 }
627 
628 /**
629  * @brief Advance the internal RNG seed and return the new value
630  * @return RNG seed
631  */
AdvanceRndSeed()632 Sint32 AdvanceRndSeed()
633 {
634 	sglGameSeed = (RndMult * static_cast<Uint32>(sglGameSeed)) + RndInc;
635 	return abs(sglGameSeed);
636 }
637 
638 /**
639  * @brief Get the current RNG seed
640  * @return RNG seed
641  */
GetRndSeed()642 Sint32 GetRndSeed()
643 {
644 	return abs(sglGameSeed);
645 }
646 
647 /**
648  * @brief Main RNG function
649  * @param idx Unused
650  * @param v The upper limit for the return value
651  * @return A random number from 0 to (v-1)
652  */
random_(BYTE idx,Sint32 v)653 Sint32 random_(BYTE idx, Sint32 v)
654 {
655 	if (v <= 0)
656 		return 0;
657 	if (v < 0xFFFF)
658 		return (AdvanceRndSeed() >> 16) % v;
659 	return AdvanceRndSeed() % v;
660 }
661 
662 /**
663  * @brief Multithreaded safe malloc
664  * @param dwBytes Byte size to allocate
665  */
DiabloAllocPtr(DWORD dwBytes)666 BYTE *DiabloAllocPtr(DWORD dwBytes)
667 {
668 	BYTE *buf;
669 
670 	sgMemCrit.Enter();
671 	buf = (BYTE *)SMemAlloc(dwBytes, __FILE__, __LINE__, 0);
672 	sgMemCrit.Leave();
673 
674 	if (buf == NULL) {
675 		const char *text = "System memory exhausted.\n"
676 		                   "Make sure you have at least 64MB of free system memory before running the game";
677 		ErrDlg("Out of Memory Error", text, __FILE__, __LINE__);
678 	}
679 
680 	return buf;
681 }
682 
683 /**
684  * @brief Multithreaded safe memfree
685  * @param p Memory pointer to free
686  */
mem_free_dbg(void * p)687 void mem_free_dbg(void *p)
688 {
689 	if (p) {
690 		sgMemCrit.Enter();
691 		SMemFree(p, __FILE__, __LINE__, 0);
692 		sgMemCrit.Leave();
693 	}
694 }
695 
696 /**
697  * @brief Load a file in to a buffer
698  * @param pszName Path of file
699  * @param pdwFileLen Will be set to file size if non-NULL
700  * @return Buffer with content of file
701  */
LoadFileInMem(const char * pszName,DWORD * pdwFileLen)702 BYTE *LoadFileInMem(const char *pszName, DWORD *pdwFileLen)
703 {
704 	HANDLE file;
705 	BYTE *buf;
706 	int fileLen;
707 
708 	SFileOpenFile(pszName, &file);
709 	fileLen = SFileGetFileSize(file, NULL);
710 
711 	if (pdwFileLen)
712 		*pdwFileLen = fileLen;
713 
714 	if (!fileLen)
715 		app_fatal("Zero length SFILE:\n%s", pszName);
716 
717 	buf = (BYTE *)DiabloAllocPtr(fileLen);
718 
719 	SFileReadFile(file, buf, fileLen, NULL, NULL);
720 	SFileCloseFile(file);
721 
722 	return buf;
723 }
724 
725 /**
726  * @brief Load a file in to the given buffer
727  * @param pszName Path of file
728  * @param p Target buffer
729  * @return Size of file
730  */
LoadFileWithMem(const char * pszName,BYTE * p)731 DWORD LoadFileWithMem(const char *pszName, BYTE *p)
732 {
733 	DWORD dwFileLen;
734 	HANDLE hsFile;
735 
736 	assert(pszName);
737 	if (p == NULL) {
738 		app_fatal("LoadFileWithMem(NULL):\n%s", pszName);
739 	}
740 
741 	SFileOpenFile(pszName, &hsFile);
742 
743 	dwFileLen = SFileGetFileSize(hsFile, NULL);
744 	if (dwFileLen == 0) {
745 		app_fatal("Zero length SFILE:\n%s", pszName);
746 	}
747 
748 	SFileReadFile(hsFile, p, dwFileLen, NULL, NULL);
749 	SFileCloseFile(hsFile);
750 
751 	return dwFileLen;
752 }
753 
754 /**
755  * @brief Apply the color swaps to a CL2 sprite
756  * @param p CL2 buffer
757  * @param ttbl Palette translation table
758  * @param nCel Frame number in CL2 file
759  */
Cl2ApplyTrans(BYTE * p,BYTE * ttbl,int nCel)760 void Cl2ApplyTrans(BYTE *p, BYTE *ttbl, int nCel)
761 {
762 	int i, nDataSize;
763 	char width;
764 	BYTE *dst;
765 
766 	assert(p != NULL);
767 	assert(ttbl != NULL);
768 
769 	for (i = 1; i <= nCel; i++) {
770 		dst = CelGetFrame(p, i, &nDataSize) + 10;
771 		nDataSize -= 10;
772 		while (nDataSize) {
773 			width = *dst++;
774 			nDataSize--;
775 			assert(nDataSize >= 0);
776 			if (width < 0) {
777 				width = -width;
778 				if (width > 65) {
779 					nDataSize--;
780 					assert(nDataSize >= 0);
781 					*dst = ttbl[*dst];
782 					dst++;
783 				} else {
784 					nDataSize -= width;
785 					assert(nDataSize >= 0);
786 					while (width--) {
787 						*dst = ttbl[*dst];
788 						dst++;
789 					}
790 				}
791 			}
792 		}
793 	}
794 }
795 
796 /**
797  * @brief Blit CL2 sprite to the given buffer
798  * @param out Target buffer
799  * @param sx Target buffer coordinate
800  * @param sy Target buffer coordinate
801  * @param pRLEBytes CL2 pixel stream (run-length encoded)
802  * @param nDataSize Size of CL2 in bytes
803  * @param nWidth Width of sprite
804  */
Cl2BlitSafe(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth)805 static void Cl2BlitSafe(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth)
806 {
807 	int w;
808 	char width;
809 	BYTE fill;
810 	BYTE *src, *dst;
811 
812 	src = pRLEBytes;
813 	dst = out.at(sx, sy);
814 	w = nWidth;
815 
816 	while (nDataSize) {
817 		width = *src++;
818 		nDataSize--;
819 		if (width < 0) {
820 			width = -width;
821 			if (width > 65) {
822 				width -= 65;
823 				nDataSize--;
824 				fill = *src++;
825 				if (dst < out.end() && dst > out.begin()) {
826 					w -= width;
827 					while (width) {
828 						*dst = fill;
829 						dst++;
830 						width--;
831 					}
832 					if (!w) {
833 						w = nWidth;
834 						dst -= out.pitch() + w;
835 					}
836 					continue;
837 				}
838 			} else {
839 				nDataSize -= width;
840 				if (dst < out.end() && dst > out.begin()) {
841 					w -= width;
842 					while (width) {
843 						*dst = *src;
844 						src++;
845 						dst++;
846 						width--;
847 					}
848 					if (!w) {
849 						w = nWidth;
850 						dst -= out.pitch() + w;
851 					}
852 					continue;
853 				} else {
854 					src += width;
855 				}
856 			}
857 		}
858 		while (width) {
859 			if (width > w) {
860 				dst += w;
861 				width -= w;
862 				w = 0;
863 			} else {
864 				dst += width;
865 				w -= width;
866 				width = 0;
867 			}
868 			if (!w) {
869 				w = nWidth;
870 				dst -= out.pitch() + w;
871 			}
872 		}
873 	}
874 }
875 
876 /**
877  * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer
878  * @param out Target buffer
879  * @param sx Target buffer coordinate
880  * @param sy Target buffer coordinate
881  * @param pRLEBytes CL2 pixel stream (run-length encoded)
882  * @param nDataSize Size of CL2 in bytes
883  * @param nWidth Width of sprite
884  * @param col Color index from current palette
885  */
Cl2BlitOutlineSafe(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth,BYTE col)886 static void Cl2BlitOutlineSafe(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE col)
887 {
888 	int w;
889 	char width;
890 	BYTE *src, *dst;
891 
892 	src = pRLEBytes;
893 	dst = out.at(sx, sy);
894 	w = nWidth;
895 
896 	while (nDataSize) {
897 		width = *src++;
898 		nDataSize--;
899 		if (width < 0) {
900 			width = -width;
901 			if (width > 65) {
902 				width -= 65;
903 				nDataSize--;
904 				if (*src++ && dst < out.end() && dst > out.begin()) {
905 					w -= width;
906 					dst[-1] = col;
907 					dst[width] = col;
908 					while (width) {
909 						dst[-out.pitch()] = col;
910 						dst[out.pitch()] = col;
911 						dst++;
912 						width--;
913 					}
914 					if (!w) {
915 						w = nWidth;
916 						dst -= out.pitch() + w;
917 					}
918 					continue;
919 				}
920 			} else {
921 				nDataSize -= width;
922 				if (dst < out.end() && dst > out.begin()) {
923 					w -= width;
924 					while (width) {
925 						if (*src++) {
926 							dst[-1] = col;
927 							dst[1] = col;
928 							dst[-out.pitch()] = col;
929 							// BUGFIX: only set `if (dst+out.pitch() < out.end())`
930 							dst[out.pitch()] = col;
931 						}
932 						dst++;
933 						width--;
934 					}
935 					if (!w) {
936 						w = nWidth;
937 						dst -= out.pitch() + w;
938 					}
939 					continue;
940 				} else {
941 					src += width;
942 				}
943 			}
944 		}
945 		while (width) {
946 			if (width > w) {
947 				dst += w;
948 				width -= w;
949 				w = 0;
950 			} else {
951 				dst += width;
952 				w -= width;
953 				width = 0;
954 			}
955 			if (!w) {
956 				w = nWidth;
957 				dst -= out.pitch() + w;
958 			}
959 		}
960 	}
961 }
962 
963 /**
964  * @brief Blit CL2 sprite, and apply lighting, to the given buffer
965  * @param out Target buffer
966  * @param sx Target buffer coordinate
967  * @param sy Target buffer coordinate
968  * @param pRLEBytes CL2 pixel stream (run-length encoded)
969  * @param nDataSize Size of CL2 in bytes
970  * @param nWidth With of CL2 sprite
971  * @param pTable Light color table
972  */
Cl2BlitLightSafe(CelOutputBuffer out,int sx,int sy,BYTE * pRLEBytes,int nDataSize,int nWidth,BYTE * pTable)973 static void Cl2BlitLightSafe(CelOutputBuffer out, int sx, int sy, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *pTable)
974 {
975 	int w, spriteWidth;
976 	char width;
977 	BYTE fill;
978 	BYTE *src, *dst;
979 
980 	src = pRLEBytes;
981 	dst = out.at(sx, sy);
982 	w = nWidth;
983 	spriteWidth = nWidth;
984 
985 	while (nDataSize) {
986 		width = *src++;
987 		nDataSize--;
988 		if (width < 0) {
989 			width = -width;
990 			if (width > 65) {
991 				width -= 65;
992 				nDataSize--;
993 				fill = pTable[*src++];
994 				if (dst < out.end() && dst > out.begin()) {
995 					w -= width;
996 					while (width) {
997 						*dst = fill;
998 						dst++;
999 						width--;
1000 					}
1001 					if (w == 0) {
1002 						w = spriteWidth;
1003 						dst -= out.pitch() + w;
1004 					}
1005 					continue;
1006 				}
1007 			} else {
1008 				nDataSize -= width;
1009 				if (dst < out.end() && dst > out.begin()) {
1010 					w -= width;
1011 					while (width) {
1012 						*dst = pTable[*src];
1013 						src++;
1014 						dst++;
1015 						width--;
1016 					}
1017 					if (w == 0) {
1018 						w = spriteWidth;
1019 						dst -= out.pitch() + w;
1020 					}
1021 					continue;
1022 				} else {
1023 					src += width;
1024 				}
1025 			}
1026 		}
1027 		while (width) {
1028 			if (width > w) {
1029 				dst += w;
1030 				width -= w;
1031 				w = 0;
1032 			} else {
1033 				dst += width;
1034 				w -= width;
1035 				width = 0;
1036 			}
1037 			if (w == 0) {
1038 				w = spriteWidth;
1039 				dst -= out.pitch() + w;
1040 			}
1041 		}
1042 	}
1043 }
1044 
Cl2Draw(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)1045 void Cl2Draw(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
1046 {
1047 	BYTE *pRLEBytes;
1048 	int nDataSize;
1049 
1050 	assert(pCelBuff != NULL);
1051 	assert(nCel > 0);
1052 
1053 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
1054 
1055 	Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, nWidth);
1056 }
1057 
Cl2DrawOutline(CelOutputBuffer out,BYTE col,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)1058 void Cl2DrawOutline(CelOutputBuffer out, BYTE col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
1059 {
1060 	int nDataSize;
1061 	BYTE *pRLEBytes;
1062 
1063 	assert(pCelBuff != NULL);
1064 	assert(nCel > 0);
1065 
1066 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
1067 
1068 	out = out.subregionY(0, out.h() - 1);
1069 	Cl2BlitOutlineSafe(out, sx, sy, pRLEBytes, nDataSize, nWidth, col);
1070 }
1071 
Cl2DrawLightTbl(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth,char light)1072 void Cl2DrawLightTbl(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light)
1073 {
1074 	int nDataSize, idx;
1075 	BYTE *pRLEBytes;
1076 
1077 	assert(pCelBuff != NULL);
1078 	assert(nCel > 0);
1079 
1080 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
1081 
1082 	idx = light4flag ? 1024 : 4096;
1083 	if (light == 2)
1084 		idx += 256; // gray colors
1085 	if (light >= 4)
1086 		idx += (light - 1) << 8;
1087 
1088 	Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, nWidth, &pLightTbl[idx]);
1089 }
1090 
Cl2DrawLight(CelOutputBuffer out,int sx,int sy,BYTE * pCelBuff,int nCel,int nWidth)1091 void Cl2DrawLight(CelOutputBuffer out, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
1092 {
1093 	int nDataSize;
1094 	BYTE *pRLEBytes;
1095 
1096 	assert(pCelBuff != NULL);
1097 	assert(nCel > 0);
1098 
1099 	pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize);
1100 
1101 	if (light_table_index)
1102 		Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, nWidth, &pLightTbl[light_table_index * 256]);
1103 	else
1104 		Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, nWidth);
1105 }
1106 
1107 /**
1108  * @brief Fade to black and play a video
1109  * @param pszMovie file path of movie
1110  */
PlayInGameMovie(const char * pszMovie)1111 void PlayInGameMovie(const char *pszMovie)
1112 {
1113 	PaletteFadeOut(8);
1114 	play_movie(pszMovie, FALSE);
1115 	ClearScreenBuffer();
1116 	force_redraw = 255;
1117 	scrollrt_draw_game_screen(TRUE);
1118 	PaletteFadeIn(8);
1119 	force_redraw = 255;
1120 }
1121 
1122 DEVILUTION_END_NAMESPACE
1123