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