1 #include <cstddef>
2 #include <cstdint>
3 #include <string>
4
5 #include "all.h"
6 #include "options.h"
7 #include "paths.h"
8 #include "../3rdParty/Storm/Source/storm.h"
9
10 #if !SDL_VERSION_ATLEAST(2, 0, 4)
11 #include <queue>
12 #endif
13
14 #include "display.h"
15 #include "stubs.h"
16 #include "Radon.hpp"
17 #include <SDL.h>
18 #include <SDL_endian.h>
19 #include <SDL_mixer.h>
20 #include <smacker.h>
21
22 #include "DiabloUI/diabloui.h"
23
24 namespace dvl {
25
26 DWORD nLastError = 0;
27
28 namespace {
29
30 bool directFileAccess = false;
31 std::string *SBasePath = NULL;
32
33 } // namespace
34
35 #ifdef USE_SDL1
36 static bool IsSVidVideoMode = false;
37 #endif
38
getIni()39 radon::File &getIni()
40 {
41 static radon::File ini(GetConfigPath() + "diablo.ini");
42 return ini;
43 }
44
45 // Converts ASCII characters to lowercase
46 // Converts slash (0x2F) / backslash (0x5C) to system file-separator
47 unsigned char AsciiToLowerTable_Path[256] = {
48 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
49 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
50 #ifdef _WIN32
51 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,
52 #else
53 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
54 #endif
55 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
56 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
57 #ifdef _WIN32
58 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
59 #else
60 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x2F, 0x5D, 0x5E, 0x5F,
61 #endif
62 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
63 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
64 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
65 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
66 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
67 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
68 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
69 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
70 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
71 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
72 };
73
SFileOpenFile(const char * filename,HANDLE * phFile)74 BOOL SFileOpenFile(const char *filename, HANDLE *phFile)
75 {
76 bool result = false;
77
78 if (directFileAccess && SBasePath != NULL) {
79 std::string path = *SBasePath + filename;
80 for (std::size_t i = SBasePath->size(); i < path.size(); ++i)
81 path[i] = AsciiToLowerTable_Path[static_cast<unsigned char>(path[i])];
82 result = SFileOpenFileEx((HANDLE)0, path.c_str(), SFILE_OPEN_LOCAL_FILE, phFile);
83 }
84
85 if (!result && devilutionx_mpq != NULL) {
86 result = SFileOpenFileEx((HANDLE)devilutionx_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
87 }
88 if (gbIsHellfire) {
89 if (!result && hfopt2_mpq != NULL) {
90 result = SFileOpenFileEx((HANDLE)hfopt2_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
91 }
92 if (!result && hfopt1_mpq != NULL) {
93 result = SFileOpenFileEx((HANDLE)hfopt1_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
94 }
95 if (!result && hfvoice_mpq != NULL) {
96 result = SFileOpenFileEx((HANDLE)hfvoice_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
97 }
98 if (!result && hfmusic_mpq != NULL) {
99 result = SFileOpenFileEx((HANDLE)hfmusic_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
100 }
101 if (!result && hfbarb_mpq != NULL) {
102 result = SFileOpenFileEx((HANDLE)hfbarb_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
103 }
104 if (!result && hfbard_mpq != NULL) {
105 result = SFileOpenFileEx((HANDLE)hfbard_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
106 }
107 if (!result && hfmonk_mpq != NULL) {
108 result = SFileOpenFileEx((HANDLE)hfmonk_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
109 }
110 if (!result) {
111 result = SFileOpenFileEx((HANDLE)hellfire_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
112 }
113 }
114 if (!result && patch_rt_mpq != NULL) {
115 result = SFileOpenFileEx((HANDLE)patch_rt_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
116 }
117 if (!result && spawn_mpq != NULL) {
118 result = SFileOpenFileEx((HANDLE)spawn_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
119 }
120 if (!result && diabdat_mpq != NULL) {
121 result = SFileOpenFileEx((HANDLE)diabdat_mpq, filename, SFILE_OPEN_FROM_MPQ, phFile);
122 }
123
124 if (!result || !*phFile) {
125 SDL_Log("%s: Not found: %s", __FUNCTION__, filename);
126 }
127 return result;
128 }
129
SBmpLoadImage(const char * pszFileName,SDL_Color * pPalette,BYTE * pBuffer,DWORD dwBuffersize,DWORD * pdwWidth,DWORD * dwHeight,DWORD * pdwBpp)130 BOOL SBmpLoadImage(const char *pszFileName, SDL_Color *pPalette, BYTE *pBuffer, DWORD dwBuffersize, DWORD *pdwWidth, DWORD *dwHeight, DWORD *pdwBpp)
131 {
132 HANDLE hFile;
133 size_t size;
134 PCXHEADER pcxhdr;
135 BYTE paldata[256][3];
136 BYTE *dataPtr, *fileBuffer;
137 BYTE byte;
138
139 if (pdwWidth)
140 *pdwWidth = 0;
141 if (dwHeight)
142 *dwHeight = 0;
143 if (pdwBpp)
144 *pdwBpp = 0;
145
146 if (!pszFileName || !*pszFileName) {
147 return false;
148 }
149
150 if (pBuffer && !dwBuffersize) {
151 return false;
152 }
153
154 if (!pPalette && !pBuffer && !pdwWidth && !dwHeight) {
155 return false;
156 }
157
158 if (!SFileOpenFile(pszFileName, &hFile)) {
159 return false;
160 }
161
162 while (strchr(pszFileName, 92))
163 pszFileName = strchr(pszFileName, 92) + 1;
164
165 while (strchr(pszFileName + 1, 46))
166 pszFileName = strchr(pszFileName, 46);
167
168 // omit all types except PCX
169 if (!pszFileName || strcasecmp(pszFileName, ".pcx")) {
170 return false;
171 }
172
173 if (!SFileReadFile(hFile, &pcxhdr, 128, 0, 0)) {
174 SFileCloseFile(hFile);
175 return false;
176 }
177
178 int width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1;
179 int height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1;
180
181 // If the given buffer is larger than width * height, assume the extra data
182 // is scanline padding.
183 //
184 // This is useful because in SDL the pitch size is often slightly larger
185 // than image width for efficiency.
186 const int x_skip = dwBuffersize / height - width;
187
188 if (pdwWidth)
189 *pdwWidth = width;
190 if (dwHeight)
191 *dwHeight = height;
192 if (pdwBpp)
193 *pdwBpp = pcxhdr.BitsPerPixel;
194
195 if (!pBuffer) {
196 SFileSetFilePointer(hFile, 0, NULL, DVL_FILE_END);
197 fileBuffer = NULL;
198 } else {
199 const auto pos = SFileGetFilePointer(hFile);
200 const auto end = SFileSetFilePointer(hFile, 0, DVL_FILE_END);
201 const auto begin = SFileSetFilePointer(hFile, pos, DVL_FILE_BEGIN);
202 size = end - begin;
203 fileBuffer = (BYTE *)malloc(size);
204 }
205
206 if (fileBuffer) {
207 SFileReadFile(hFile, fileBuffer, size, 0, 0);
208 dataPtr = fileBuffer;
209
210 for (int j = 0; j < height; j++) {
211 for (int x = 0; x < width; dataPtr++) {
212 byte = *dataPtr;
213 if (byte < 0xC0) {
214 *pBuffer = byte;
215 pBuffer++;
216 x++;
217 continue;
218 }
219 dataPtr++;
220
221 for (int i = 0; i < (byte & 0x3F); i++) {
222 *pBuffer = *dataPtr;
223 pBuffer++;
224 x++;
225 }
226 }
227 // Skip the pitch padding.
228 pBuffer += x_skip;
229 }
230
231 free(fileBuffer);
232 }
233
234 if (pPalette && pcxhdr.BitsPerPixel == 8) {
235 const auto pos = SFileSetFilePointer(hFile, -768, DVL_FILE_CURRENT);
236 if (pos == static_cast<std::uint64_t>(-1)) {
237 SDL_Log("SFileSetFilePointer error: %ud", (unsigned int)SErrGetLastError());
238 }
239 SFileReadFile(hFile, paldata, 768, 0, NULL);
240
241 for (int i = 0; i < 256; i++) {
242 pPalette[i].r = paldata[i][0];
243 pPalette[i].g = paldata[i][1];
244 pPalette[i].b = paldata[i][2];
245 #ifndef USE_SDL1
246 pPalette[i].a = SDL_ALPHA_OPAQUE;
247 #endif
248 }
249 }
250
251 SFileCloseFile(hFile);
252
253 return true;
254 }
255
SMemAlloc(unsigned int amount,const char * logfilename,int logline,int defaultValue)256 void *SMemAlloc(unsigned int amount, const char *logfilename, int logline, int defaultValue)
257 {
258 assert(amount != -1u);
259 return malloc(amount);
260 }
261
SMemFree(void * location,const char * logfilename,int logline,char defaultValue)262 BOOL SMemFree(void *location, const char *logfilename, int logline, char defaultValue)
263 {
264 assert(location);
265 free(location);
266 return true;
267 }
268
getIniBool(const char * sectionName,const char * keyName,bool defaultValue)269 bool getIniBool(const char *sectionName, const char *keyName, bool defaultValue)
270 {
271 char string[2];
272
273 if (!getIniValue(sectionName, keyName, string, 2))
274 return defaultValue;
275
276 return strtol(string, NULL, 10) != 0;
277 }
278
getIniValue(const char * sectionName,const char * keyName,char * string,int stringSize,const char * defaultString)279 bool getIniValue(const char *sectionName, const char *keyName, char *string, int stringSize, const char *defaultString)
280 {
281 strncpy(string, defaultString, stringSize);
282
283 radon::Section *section = getIni().getSection(sectionName);
284 if (!section)
285 return false;
286
287 radon::Key *key = section->getKey(keyName);
288 if (!key)
289 return false;
290
291 std::string value = key->getStringValue();
292
293 if (string != NULL)
294 strncpy(string, value.c_str(), stringSize);
295
296 return true;
297 }
298
setIniValue(const char * sectionName,const char * keyName,const char * value,int len)299 void setIniValue(const char *sectionName, const char *keyName, const char *value, int len)
300 {
301 radon::File &ini = getIni();
302
303 radon::Section *section = ini.getSection(sectionName);
304 if (!section) {
305 ini.addSection(sectionName);
306 section = ini.getSection(sectionName);
307 }
308
309 std::string stringValue(value, len ? len : strlen(value));
310
311 radon::Key *key = section->getKey(keyName);
312 if (!key) {
313 section->addKey(radon::Key(keyName, stringValue));
314 } else {
315 key->setValue(stringValue);
316 }
317 }
318
SaveIni()319 void SaveIni()
320 {
321 getIni().saveToFile();
322 }
323
getIniInt(const char * keyname,const char * valuename,int defaultValue)324 int getIniInt(const char *keyname, const char *valuename, int defaultValue)
325 {
326 char string[10];
327 if (!getIniValue(keyname, valuename, string, sizeof(string))) {
328 return defaultValue;
329 }
330
331 return strtol(string, NULL, sizeof(string));
332 }
333
setIniInt(const char * keyname,const char * valuename,int value)334 void setIniInt(const char *keyname, const char *valuename, int value)
335 {
336 char str[10];
337 sprintf(str, "%d", value);
338 setIniValue(keyname, valuename, str);
339 }
340
341 double SVidFrameEnd;
342 double SVidFrameLength;
343 char SVidAudioDepth;
344 double SVidVolume;
345 BYTE SVidLoop;
346 smk SVidSMK;
347 SDL_Color SVidPreviousPalette[256];
348 SDL_Palette *SVidPalette;
349 SDL_Surface *SVidSurface;
350 BYTE *SVidBuffer;
351 unsigned long SVidWidth, SVidHeight;
352
353 #if SDL_VERSION_ATLEAST(2, 0, 4)
354 SDL_AudioDeviceID deviceId;
HaveAudio()355 static bool HaveAudio()
356 {
357 return deviceId != 0;
358 }
359 #else
HaveAudio()360 static bool HaveAudio()
361 {
362 return SDL_GetAudioStatus() != SDL_AUDIO_STOPPED;
363 }
364 #endif
365
SVidRestartMixer()366 void SVidRestartMixer()
367 {
368 if (Mix_OpenAudio(22050, AUDIO_S16LSB, 2, 1024) < 0) {
369 SDL_Log(Mix_GetError());
370 }
371 Mix_AllocateChannels(25);
372 Mix_ReserveChannels(1);
373 }
374
375 #if !SDL_VERSION_ATLEAST(2, 0, 4)
376 struct AudioQueueItem {
377 unsigned char *data;
378 unsigned long len;
379 const unsigned char *pos;
380 };
381
382 class AudioQueue {
383 public:
Callback(void * userdata,Uint8 * out,int out_len)384 static void Callback(void *userdata, Uint8 *out, int out_len)
385 {
386 static_cast<AudioQueue *>(userdata)->Dequeue(out, out_len);
387 }
388
Subscribe(SDL_AudioSpec * spec)389 void Subscribe(SDL_AudioSpec *spec)
390 {
391 spec->userdata = this;
392 spec->callback = AudioQueue::Callback;
393 }
394
Enqueue(const unsigned char * data,unsigned long len)395 void Enqueue(const unsigned char *data, unsigned long len)
396 {
397 #if SDL_VERSION_ATLEAST(2, 0, 4)
398 SDL_LockAudioDevice(deviceId);
399 EnqueueUnsafe(data, len);
400 SDL_UnlockAudioDevice(deviceId);
401 #else
402 SDL_LockAudio();
403 EnqueueUnsafe(data, len);
404 SDL_UnlockAudio();
405 #endif
406 }
407
Clear()408 void Clear()
409 {
410 while (!queue_.empty())
411 Pop();
412 }
413
414 private:
EnqueueUnsafe(const unsigned char * data,unsigned long len)415 void EnqueueUnsafe(const unsigned char *data, unsigned long len)
416 {
417 AudioQueueItem item;
418 item.data = new unsigned char[len];
419 memcpy(item.data, data, len * sizeof(item.data[0]));
420 item.len = len;
421 item.pos = item.data;
422 queue_.push(item);
423 }
424
Dequeue(Uint8 * out,int out_len)425 void Dequeue(Uint8 *out, int out_len)
426 {
427 SDL_memset(out, 0, sizeof(out[0]) * out_len);
428 AudioQueueItem *item;
429 while ((item = Next()) != NULL) {
430 if (static_cast<unsigned long>(out_len) <= item->len) {
431 SDL_MixAudio(out, item->pos, out_len, SDL_MIX_MAXVOLUME);
432 item->pos += out_len;
433 item->len -= out_len;
434 return;
435 }
436
437 SDL_MixAudio(out, item->pos, item->len, SDL_MIX_MAXVOLUME);
438 out += item->len;
439 out_len -= item->len;
440 Pop();
441 }
442 }
443
Next()444 AudioQueueItem *Next()
445 {
446 while (!queue_.empty() && queue_.front().len == 0)
447 Pop();
448 if (queue_.empty())
449 return NULL;
450 return &queue_.front();
451 }
452
Pop()453 void Pop()
454 {
455 delete[] queue_.front().data;
456 queue_.pop();
457 }
458
459 std::queue<AudioQueueItem> queue_;
460 };
461
462 static AudioQueue *sVidAudioQueue = new AudioQueue();
463 #endif
464
SVidPlayBegin(const char * filename,int a2,int a3,int a4,int a5,int flags,HANDLE * video)465 void SVidPlayBegin(const char *filename, int a2, int a3, int a4, int a5, int flags, HANDLE *video)
466 {
467 if (flags & 0x10000 || flags & 0x20000000) {
468 return;
469 }
470
471 SVidLoop = false;
472 if (flags & 0x40000)
473 SVidLoop = true;
474 bool enableVideo = !(flags & 0x100000);
475 bool enableAudio = !(flags & 0x1000000);
476 //0x8 // Non-interlaced
477 //0x200, 0x800 // Upscale video
478 //0x80000 // Center horizontally
479 //0x800000 // Edge detection
480 //0x200800 // Clear FB
481
482 SFileOpenFile(filename, video);
483
484 int bytestoread = SFileGetFileSize(*video, 0);
485 SVidBuffer = DiabloAllocPtr(bytestoread);
486 SFileReadFile(*video, SVidBuffer, bytestoread, NULL, 0);
487
488 SVidSMK = smk_open_memory(SVidBuffer, bytestoread);
489 if (SVidSMK == NULL) {
490 return;
491 }
492
493 unsigned char channels[7], depth[7];
494 unsigned long rate[7];
495 smk_info_audio(SVidSMK, NULL, channels, depth, rate);
496 if (enableAudio && depth[0] != 0) {
497 smk_enable_audio(SVidSMK, 0, enableAudio);
498 SDL_AudioSpec audioFormat;
499 SDL_zero(audioFormat);
500 audioFormat.freq = rate[0];
501 audioFormat.format = depth[0] == 16 ? AUDIO_S16SYS : AUDIO_U8;
502 SVidAudioDepth = depth[0];
503 audioFormat.channels = channels[0];
504
505 SVidVolume = sgOptions.Audio.nSoundVolume - VOLUME_MIN;
506 SVidVolume /= -VOLUME_MIN;
507
508 Mix_CloseAudio();
509
510 #if SDL_VERSION_ATLEAST(2, 0, 4)
511 deviceId = SDL_OpenAudioDevice(NULL, 0, &audioFormat, NULL, 0);
512 if (deviceId == 0) {
513 ErrSdl();
514 }
515
516 SDL_PauseAudioDevice(deviceId, 0); /* start audio playing. */
517 #else
518 sVidAudioQueue->Subscribe(&audioFormat);
519 if (SDL_OpenAudio(&audioFormat, NULL) != 0) {
520 ErrSdl();
521 }
522 SDL_PauseAudio(0);
523 #endif
524 }
525
526 unsigned long nFrames;
527 smk_info_all(SVidSMK, NULL, &nFrames, &SVidFrameLength);
528 smk_info_video(SVidSMK, &SVidWidth, &SVidHeight, NULL);
529
530 smk_enable_video(SVidSMK, enableVideo);
531 smk_first(SVidSMK); // Decode first frame
532
533 smk_info_video(SVidSMK, &SVidWidth, &SVidHeight, NULL);
534 #ifndef USE_SDL1
535 if (renderer) {
536 SDL_DestroyTexture(texture);
537 texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, SVidWidth, SVidHeight);
538 if (texture == NULL) {
539 ErrSdl();
540 }
541 if (SDL_RenderSetLogicalSize(renderer, SVidWidth, SVidHeight) <= -1) {
542 ErrSdl();
543 }
544 }
545 #else
546 // Set the video mode close to the SVid resolution while preserving aspect ratio.
547 {
548 const SDL_Surface *display = SDL_GetVideoSurface();
549 IsSVidVideoMode = (display->flags & (SDL_FULLSCREEN | SDL_NOFRAME)) != 0;
550
551 if (IsSVidVideoMode) {
552 /* Get available fullscreen/hardware modes */
553 SDL_Rect **modes = SDL_ListModes(NULL, display->flags);
554
555 /* Check is there are any modes available */
556 if (modes == (SDL_Rect **)0) {
557 IsSVidVideoMode = false;
558 }
559
560 /* Check if our resolution is restricted */
561 if (modes != (SDL_Rect **)-1) {
562 // Search for a usable video mode
563 bool UsableModeFound = false;
564 for (int i = 0; modes[i]; i++) {
565 if (modes[i]->w == SVidWidth || modes[i]->h == SVidHeight) {
566 UsableModeFound = true;
567 break;
568 }
569 }
570 IsSVidVideoMode = UsableModeFound;
571 }
572 }
573
574 if (IsSVidVideoMode) {
575 int w, h;
576 if (display->w * SVidWidth > display->h * SVidHeight) {
577 w = SVidWidth;
578 h = SVidWidth * display->h / display->w;
579 } else {
580 w = SVidHeight * display->w / display->h;
581 h = SVidHeight;
582 }
583 SetVideoMode(w, h, display->format->BitsPerPixel, display->flags);
584 }
585 }
586 #endif
587 memcpy(SVidPreviousPalette, orig_palette, sizeof(SVidPreviousPalette));
588
589 // Copy frame to buffer
590 SVidSurface = SDL_CreateRGBSurfaceWithFormatFrom(
591 (unsigned char *)smk_get_video(SVidSMK),
592 SVidWidth,
593 SVidHeight,
594 8,
595 SVidWidth,
596 SDL_PIXELFORMAT_INDEX8);
597 if (SVidSurface == NULL) {
598 ErrSdl();
599 }
600
601 SVidPalette = SDL_AllocPalette(256);
602 if (SVidPalette == NULL) {
603 ErrSdl();
604 }
605 if (SDLC_SetSurfaceColors(SVidSurface, SVidPalette) <= -1) {
606 ErrSdl();
607 }
608
609 SVidFrameEnd = SDL_GetTicks() * 1000 + SVidFrameLength;
610 SDL_FillRect(GetOutputSurface(), NULL, 0x000000);
611 }
612
SVidLoadNextFrame()613 BOOL SVidLoadNextFrame()
614 {
615 SVidFrameEnd += SVidFrameLength;
616
617 if (smk_next(SVidSMK) == SMK_DONE) {
618 if (!SVidLoop) {
619 return false;
620 }
621
622 smk_first(SVidSMK);
623 }
624
625 return true;
626 }
627
SVidApplyVolume(const unsigned char * raw,unsigned long rawLen)628 unsigned char *SVidApplyVolume(const unsigned char *raw, unsigned long rawLen)
629 {
630 unsigned char *scaled = (unsigned char *)malloc(rawLen);
631
632 if (SVidAudioDepth == 16) {
633 for (unsigned long i = 0; i < rawLen / 2; i++)
634 ((Sint16 *)scaled)[i] = ((Sint16 *)raw)[i] * SVidVolume;
635 } else {
636 for (unsigned long i = 0; i < rawLen; i++)
637 scaled[i] = raw[i] * SVidVolume;
638 }
639
640 return (unsigned char *)scaled;
641 }
642
SVidPlayContinue(void)643 BOOL SVidPlayContinue(void)
644 {
645 if (smk_palette_updated(SVidSMK)) {
646 SDL_Color colors[256];
647 const unsigned char *palette_data = smk_get_palette(SVidSMK);
648
649 for (int i = 0; i < 256; i++) {
650 colors[i].r = palette_data[i * 3 + 0];
651 colors[i].g = palette_data[i * 3 + 1];
652 colors[i].b = palette_data[i * 3 + 2];
653 #ifndef USE_SDL1
654 colors[i].a = SDL_ALPHA_OPAQUE;
655 #endif
656
657 orig_palette[i].r = palette_data[i * 3 + 0];
658 orig_palette[i].g = palette_data[i * 3 + 1];
659 orig_palette[i].b = palette_data[i * 3 + 2];
660 }
661 memcpy(logical_palette, orig_palette, sizeof(logical_palette));
662
663 if (SDLC_SetSurfaceAndPaletteColors(SVidSurface, SVidPalette, colors, 0, 256) <= -1) {
664 SDL_Log(SDL_GetError());
665 return false;
666 }
667 }
668
669 if (SDL_GetTicks() * 1000 >= SVidFrameEnd) {
670 return SVidLoadNextFrame(); // Skip video and audio if the system is to slow
671 }
672
673 if (HaveAudio()) {
674 unsigned long len = smk_get_audio_size(SVidSMK, 0);
675 unsigned char *audio = SVidApplyVolume(smk_get_audio(SVidSMK, 0), len);
676 #if SDL_VERSION_ATLEAST(2, 0, 4)
677 if (SDL_QueueAudio(deviceId, audio, len) <= -1) {
678 SDL_Log(SDL_GetError());
679 return false;
680 }
681 #else
682 sVidAudioQueue->Enqueue(audio, len);
683 #endif
684 free(audio);
685 }
686
687 if (SDL_GetTicks() * 1000 >= SVidFrameEnd) {
688 return SVidLoadNextFrame(); // Skip video if the system is to slow
689 }
690
691 #ifndef USE_SDL1
692 if (renderer) {
693 if (SDL_BlitSurface(SVidSurface, NULL, GetOutputSurface(), NULL) <= -1) {
694 SDL_Log(SDL_GetError());
695 return false;
696 }
697 } else
698 #endif
699 {
700 SDL_Surface *output_surface = GetOutputSurface();
701 int factor;
702 int wFactor = output_surface->w / SVidWidth;
703 int hFactor = output_surface->h / SVidHeight;
704 if (wFactor > hFactor && (unsigned int)output_surface->h > SVidHeight) {
705 factor = hFactor;
706 } else {
707 factor = wFactor;
708 }
709 const Uint16 scaledW = SVidWidth * factor;
710 const Uint16 scaledH = SVidHeight * factor;
711 const Sint16 scaledX = (output_surface->w - scaledW) / 2;
712 const Sint16 scaledY = (output_surface->h - scaledH) / 2;
713
714 SDL_Rect pal_surface_offset = { scaledX, scaledY, scaledW, scaledH };
715 if (factor == 1) {
716 if (SDL_BlitSurface(SVidSurface, NULL, output_surface, &pal_surface_offset) <= -1) {
717 ErrSdl();
718 }
719 } else {
720 #ifdef USE_SDL1
721 SDL_Surface *tmp = SDL_ConvertSurface(SVidSurface, ghMainWnd->format, 0);
722 #else
723 Uint32 format = SDL_GetWindowPixelFormat(ghMainWnd);
724 SDL_Surface *tmp = SDL_ConvertSurfaceFormat(SVidSurface, format, 0);
725 #endif
726 if (SDL_BlitScaled(tmp, NULL, output_surface, &pal_surface_offset) <= -1) {
727 SDL_Log(SDL_GetError());
728 return false;
729 }
730 SDL_FreeSurface(tmp);
731 }
732 }
733
734 RenderPresent();
735
736 double now = SDL_GetTicks() * 1000;
737 if (now < SVidFrameEnd) {
738 SDL_Delay((SVidFrameEnd - now) / 1000); // wait with next frame if the system is too fast
739 }
740
741 return SVidLoadNextFrame();
742 }
743
SVidPlayEnd(HANDLE video)744 void SVidPlayEnd(HANDLE video)
745 {
746 if (HaveAudio()) {
747 #if SDL_VERSION_ATLEAST(2, 0, 4)
748 SDL_ClearQueuedAudio(deviceId);
749 SDL_CloseAudioDevice(deviceId);
750 deviceId = 0;
751 #else
752 SDL_CloseAudio();
753 sVidAudioQueue->Clear();
754 #endif
755 SVidRestartMixer();
756 }
757
758 if (SVidSMK)
759 smk_close(SVidSMK);
760
761 if (SVidBuffer) {
762 mem_free_dbg(SVidBuffer);
763 SVidBuffer = NULL;
764 }
765
766 SDL_FreePalette(SVidPalette);
767 SVidPalette = NULL;
768
769 SDL_FreeSurface(SVidSurface);
770 SVidSurface = NULL;
771
772 SFileCloseFile(video);
773 video = NULL;
774
775 memcpy(orig_palette, SVidPreviousPalette, sizeof(orig_palette));
776 #ifndef USE_SDL1
777 if (renderer) {
778 SDL_DestroyTexture(texture);
779 texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, gnScreenWidth, gnScreenHeight);
780 if (texture == NULL) {
781 ErrSdl();
782 }
783 if (renderer && SDL_RenderSetLogicalSize(renderer, gnScreenWidth, gnScreenHeight) <= -1) {
784 ErrSdl();
785 }
786 }
787 #else
788 if (IsSVidVideoMode)
789 SetVideoModeToPrimary(IsFullScreen(), gnScreenWidth, gnScreenHeight);
790 #endif
791 }
792
SErrGetLastError()793 DWORD SErrGetLastError()
794 {
795 return nLastError;
796 }
797
SErrSetLastError(DWORD dwErrCode)798 void SErrSetLastError(DWORD dwErrCode)
799 {
800 nLastError = dwErrCode;
801 }
802
SStrCopy(char * dest,const char * src,int max_length)803 void SStrCopy(char *dest, const char *src, int max_length)
804 {
805 strncpy(dest, src, max_length);
806 }
807
SFileSetBasePath(const char * path)808 BOOL SFileSetBasePath(const char *path)
809 {
810 if (SBasePath == NULL)
811 SBasePath = new std::string;
812 *SBasePath = path;
813 return true;
814 }
815
SFileEnableDirectAccess(BOOL enable)816 BOOL SFileEnableDirectAccess(BOOL enable)
817 {
818 directFileAccess = enable;
819 return true;
820 }
821 } // namespace dvl
822