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