1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: init.cpp
5 	Desc: contains program initialization code
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include <memory>
13 #include <ctime>
14 #include <sys/stat.h>
15 #include <sstream>
16 
17 #include "main.hpp"
18 #include "draw.hpp"
19 #include "files.hpp"
20 #include "sound.hpp"
21 #include "prng.hpp"
22 #include "hash.hpp"
23 #include "init.hpp"
24 #include "net.hpp"
25 #include "editor.hpp"
26 #include "menu.hpp"
27 #ifdef STEAMWORKS
28 #include <steam/steam_api.h>
29 #include "steam.hpp"
30 #endif
31 #include "player.hpp"
32 #include "items.hpp"
33 #include "cppfuncs.hpp"
34 
35 #ifdef USE_FMOD
36 #include "fmod.h"
37 //#include <fmod_errors.h>
38 #endif
39 
40 /*-------------------------------------------------------------------------------
41 
42 	initApp
43 
44 	initializes all the application variables and starts the engine
45 
46 -------------------------------------------------------------------------------*/
47 
48 #define LOADSTR1 language[741]
49 #define LOADSTR2 language[742]
50 #define LOADSTR3 language[743]
51 #define LOADSTR4 language[744]
52 
53 #ifdef PANDORA
54 // Pandora FBO
55 GLuint fbo_fbo = 0;
56 GLuint fbo_tex = 0;
57 GLuint fbo_trn = 0;
58 GLuint fbo_ren = 0;
59 #endif
60 
61 FILE* logfile = nullptr;
62 bool steam_init = false;
63 
initApp(char const * const title,int fullscreen)64 int initApp(char const * const title, int fullscreen)
65 {
66 	char name[128];
67 	FILE* fp;
68 	Uint32 x, c;
69 
70 	// open log file
71 	if ( !logfile )
72 	{
73 		openLogFile();
74 	}
75 
76 	for (c = 0; c < NUM_JOY_STATUS; ++c)
77 	{
78 		joystatus[c] = 0;
79 	}
80 	for (c = 0; c < NUM_JOY_TRIGGER_STATUS; ++c)
81 	{
82 		joy_trigger_status[c] = 0;
83 	}
84 
85 	// init some lists
86 	button_l.first = NULL;
87 	button_l.last = NULL;
88 	light_l.first = NULL;
89 	light_l.last = NULL;
90 	entitiesdeleted.first = NULL;
91 	entitiesdeleted.last = NULL;
92 	for ( c = 0; c < HASH_SIZE; c++ )
93 	{
94 		ttfTextHash[c].first = NULL;
95 		ttfTextHash[c].last = NULL;
96 	}
97 	map.entities = NULL;
98 	map.creatures = nullptr;
99 	map.tiles = NULL;
100 
101 	// init PHYSFS
102 	PHYSFS_init("/");
103 	PHYSFS_permitSymbolicLinks(1);
104 	if ( !PHYSFS_isInit() )
105 	{
106 		printlog("[PhysFS]: failed to initialize! Error code: %d", PHYSFS_getLastErrorCode());
107 		return 13;
108 	}
109 	else
110 	{
111 		printlog("[PhysFS]: successfully initialized, returned: %d", PHYSFS_getLastErrorCode());
112 	}
113 	if ( !PHYSFS_mount(datadir, NULL, 1) )
114 	{
115 		printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error code: %d", datadir, PHYSFS_getLastErrorCode());
116 		return 13;
117 	}
118 	if ( PHYSFS_mount(outputdir, NULL, 1) )
119 	{
120 		printlog("[PhysFS]: successfully mounted output %s folder", outputdir);
121 		if ( PHYSFS_setWriteDir(outputdir) )
122 		{
123 			PHYSFS_mkdir("savegames");
124 			PHYSFS_mkdir("crashlogs");
125 			PHYSFS_mkdir("logfiles");
126 			PHYSFS_mkdir("data");
127 			PHYSFS_mkdir("data/custom-monsters");
128 			if ( PHYSFS_mkdir("mods") )
129 			{
130 				std::string path = outputdir;
131 				path.append(PHYSFS_getDirSeparator()).append("mods");
132 				PHYSFS_setWriteDir(path.c_str());
133 				printlog("[PhysFS]: successfully set write folder %s", path.c_str());
134 			}
135 			else
136 			{
137 				printlog("[PhysFS]: unsuccessfully created mods/ folder. Error code: %d", PHYSFS_getLastErrorCode());
138 				return 13;
139 			}
140 		}
141 	}
142 	else
143 	{
144 		printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error code: %d", outputdir, PHYSFS_getLastErrorCode());
145 		return 13;
146 	}
147 
148 	// init steamworks
149 #ifdef STEAMWORKS
150 	SteamAPI_RestartAppIfNecessary(STEAM_APPID);
151 	if ( !SteamAPI_Init() )
152 	{
153 		printlog("error: failed to initialize Steamworks!\n");
154 		printlog(" make sure your steam client is running before attempting to start again.\n");
155 		return 1;
156 	}
157 	steam_init = true;
158 	g_SteamLeaderboards = new CSteamLeaderboards();
159 	g_SteamWorkshop = new CSteamWorkshop();
160 	g_SteamStatistics = new CSteamStatistics(g_SteamStats, nullptr, NUM_STEAM_STATISTICS);
161 	// Preloads mod content from a workshop fileID
162 	//gamemodsWorkshopPreloadMod(YOUR WORKSHOP FILE ID HERE, "YOUR WORKSHOP TITLE HERE");
163 #endif
164 #if defined USE_EOS
165 	EOS.readFromFile();
166 	EOS.readFromCmdLineArgs();
167 	if ( EOS.initPlatform(true) == false )
168 	{
169 		return 14;
170 	}
171 #ifndef STEAMWORKS
172 #ifdef APPLE
173 	if ( EOS.CredentialName.compare("") == 0 )
174 	{
175 		EOSFuncs::logInfo("Error, attempting to launch outside of store...");
176 		return 15;
177 	}
178 #else
179 	if ( EOS.appRequiresRestart == EOS_EResult::EOS_Success )
180 	{
181 		// restarting app
182 		EOSFuncs::logInfo("App attempting restart through store...");
183 		return 15;
184 	}
185 #endif
186 	EOS.initAuth();
187 #endif // !STEAMWORKS
188 #endif
189 
190 	window_title = title;
191 	printlog("initializing SDL...\n");
192 	if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) == -1 )
193 	{
194 		printlog("failed to initialize SDL: %s\n", SDL_GetError());
195 		return 1;
196 	}
197 	//printlog("initializing SDL_mixer. rate: %d format: %d channels: %d buffers: %d\n", audio_rate, audio_format, audio_channels, audio_buffers);
198 	/*if( Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) ) {
199 		SDL_Quit();
200 		printlog("failed to initialize SDL_mixer: %s\n", Mix_GetError());
201 		return 2;
202 	}*/
203 
204 #ifdef USE_FMOD
205 	printlog("initializing FMOD...\n");
206 	fmod_result = FMOD_System_Create(&fmod_system);
207 	if (FMODErrorCheck())
208 	{
209 		printlog("Failed to create FMOD.\n");
210 		no_sound = true;
211 	}
212 	if (!no_sound)
213 	{
214 		//FMOD_System_SetOutput(fmod_system, FMOD_OUTPUTTYPE_DSOUND);
215 		fmod_result = FMOD_System_Init(fmod_system, fmod_maxchannels, FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED, 0);
216 		if (FMODErrorCheck())
217 		{
218 			printlog("Failed to initialize FMOD.\n");
219 			no_sound = true;
220 		}
221 		if (!no_sound)
222 		{
223 			fmod_result = FMOD_System_CreateChannelGroup(fmod_system, NULL, &sound_group);
224 			if (FMODErrorCheck())
225 			{
226 				printlog("Failed to create sound channel group.\n");
227 				no_sound = true;
228 			}
229 			fmod_result = FMOD_System_CreateChannelGroup(fmod_system, NULL, &soundAmbient_group);
230 			if ( FMODErrorCheck() )
231 			{
232 				printlog("Failed to create sound ambient channel group.\n");
233 				no_sound = true;
234 			}
235 			fmod_result = FMOD_System_CreateChannelGroup(fmod_system, NULL, &soundEnvironment_group);
236 			if ( FMODErrorCheck() )
237 			{
238 				printlog("Failed to create sound environment channel group.\n");
239 				no_sound = true;
240 			}
241 			if (!no_sound)
242 			{
243 				fmod_result = FMOD_System_CreateChannelGroup(fmod_system, NULL, &music_group);
244 				if (FMODErrorCheck())
245 				{
246 					printlog("Failed to create music channel group.\n");
247 					no_sound = true;
248 				}
249 			}
250 		}
251 	}
252 #elif defined USE_OPENAL
253 	if (!no_sound)
254 	{
255 		initOPENAL();
256 	}
257 #endif
258 	printlog("initializing SDL_net...\n");
259 	if ( SDLNet_Init() < 0 )
260 	{
261 		printlog("failed to initialize SDL_net: %s\n", SDLNet_GetError());
262 		return 2;
263 	}
264 	printlog("initializing SDL_image...\n");
265 	if ( IMG_Init(IMG_INIT_PNG) != (IMG_INIT_PNG) )
266 	{
267 		printlog("failed to initialize SDL_image: %s\n", IMG_GetError());
268 		return 2;
269 	}
270 
271 	// hide cursor for game
272 	if ( game )
273 	{
274 		SDL_ShowCursor(SDL_FALSE);
275 	}
276 	SDL_StopTextInput();
277 
278 	// initialize video
279 	if ( !initVideo() )
280 	{
281 		return 3;
282 	}
283 	//SDL_EnableUNICODE(1);
284 	//SDL_WM_SetCaption(title, 0);
285 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
286 
287 	// get pointers to opengl extensions
288 #ifdef WINDOWS
289 	bool noextensions = false;
290 	if ( !softwaremode )
291 	{
292 		if ( (SDL_glGenBuffers = (PFNGLGENBUFFERSPROC)SDL_GL_GetProcAddress("glGenBuffers")) == NULL )
293 		{
294 			noextensions = true;
295 		}
296 		else if ( (SDL_glBindBuffer = (PFNGLBINDBUFFERPROC)SDL_GL_GetProcAddress("glBindBuffer")) == NULL )
297 		{
298 			noextensions = true;
299 		}
300 		else if ( (SDL_glBufferData = (PFNGLBUFFERDATAPROC)SDL_GL_GetProcAddress("glBufferData")) == NULL )
301 		{
302 			noextensions = true;
303 		}
304 		else if ( (SDL_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteBuffers")) == NULL )
305 		{
306 			noextensions = true;
307 		}
308 		else if ( (SDL_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glGenVertexArrays")) == NULL )
309 		{
310 			noextensions = true;
311 		}
312 		else if ( (SDL_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)SDL_GL_GetProcAddress("glBindVertexArray")) == NULL )
313 		{
314 			noextensions = true;
315 		}
316 		else if ( (SDL_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glDeleteVertexArrays")) == NULL )
317 		{
318 			noextensions = true;
319 		}
320 /*
321 // Unused
322 		else if ( (SDL_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray")) == NULL )
323 		{
324 			noextensions = true;
325 		}
326 		else if ( (SDL_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer")) == NULL )
327 		{
328 			noextensions = true;
329 		}
330 */
331 	}
332 	if (softwaremode)
333 	{
334 		printlog("notice: using software rendering.\n");
335 	}
336 	if ( noextensions )
337 	{
338 		printlog("warning: failed to load OpenGL extensions.\nYou may want to update your drivers or your graphics card, as performance will be reduced without these.\n");
339 		disablevbos = true;
340 	}
341 #else
342 	if (softwaremode)
343 	{
344 		printlog("notice: using software rendering.\n");
345 	}
346 #endif
347 
348 	// initialize buffers
349 	zbuffer = (real_t*) malloc(sizeof(real_t) * xres * yres);
350 	clickmap = (Entity**) malloc(sizeof(Entity*)*xres * yres);
351 	texid = (GLuint*) malloc(MAXTEXTURES * sizeof(GLuint));
352 	//vaoid = (GLuint *) malloc(MAXBUFFERS*sizeof(GLuint));
353 	//vboid = (GLuint *) malloc(MAXBUFFERS*sizeof(GLuint));
354 	allsurfaces = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*MAXTEXTURES);
355 	for ( c = 0; c < MAXTEXTURES; c++ )
356 	{
357 		allsurfaces[c] = NULL;
358 	}
359 	glGenTextures(MAXTEXTURES, texid);
360 	//SDL_glGenVertexArrays(MAXBUFFERS, vaoid);
361 	//SDL_glGenBuffers(MAXBUFFERS, vboid);
362 
363 	// load windows icon
364 #ifndef _MSC_VER
365 #if defined(WINDOWS) && defined(GCL_HICON)
366 	HINSTANCE handle = GetModuleHandle(NULL);
367 	HICON icon = LoadIcon(handle, "id");
368 	if ( icon != NULL )
369 	{
370 		SDL_SysWMinfo wminfo;
371 		SDL_VERSION( &wminfo.version );
372 		if ( SDL_GetWindowWMInfo(screen, &wminfo) == SDL_TRUE )
373 		{
374 			HWND hwnd = wminfo.info.win.window;
375 			SetClassLong(hwnd, GCL_HICON, (LONG)icon);
376 		}
377 	}
378 #endif
379 #endif
380 
381 	drawClearBuffers();
382 	GO_SwapBuffers(screen);
383 
384 	// load resources
385 	printlog("loading engine resources...\n");
386 	if ((fancyWindow_bmp = loadImage("images/system/fancyWindow.png")) == NULL)
387 	{
388 		printlog("failed to load fancyWindow.png\n");
389 		return 5;
390 	}
391 	if ((font8x8_bmp = loadImage("images/system/font8x8.png")) == NULL)
392 	{
393 		printlog("failed to load font8x8.png\n");
394 		return 5;
395 	}
396 	if ((font12x12_bmp = loadImage("images/system/font12x12.png")) == NULL)
397 	{
398 		printlog("failed to load font12x12.png\n");
399 		return 5;
400 	}
401 	if ((font16x16_bmp = loadImage("images/system/font16x16.png")) == NULL)
402 	{
403 		printlog("failed to load font16x16.png\n");
404 		return 5;
405 	}
406 
407 	// cache language entries
408 	bool cacheText = false;
409 	if (cacheText) {
410 		for (int c = 0; c < NUMLANGENTRIES; ++c) {
411 			bool foundSpecialChar = false;
412 			for (int i = 0; language[c][i] != '\0'; ++i) {
413 				if (language[c][i] == '\\' || language[c][i] == '%') {
414 					foundSpecialChar = true;
415 				}
416 			}
417 			if (foundSpecialChar) {
418 				continue;
419 			}
420 			ttfPrintText(ttf8, 0, -200, language[c]);
421 			ttfPrintText(ttf12, 0, -200, language[c]);
422 			ttfPrintText(ttf16, 0, -200, language[c]);
423 		}
424 	}
425 
426 	// print a loading message
427 	drawClearBuffers();
428 	int w, h;
429 	TTF_SizeUTF8(ttf16, LOADSTR1, &w, &h);
430 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, LOADSTR1);
431 
432 	GO_SwapBuffers(screen);
433 
434 	// load sprites
435 	printlog("loading sprites...\n");
436 	fp = openDataFile("images/sprites.txt", "r");
437 	for ( numsprites = 0; !feof(fp); numsprites++ )
438 	{
439 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
440 			{
441 				break;
442 			}
443 	}
444 	fclose(fp);
445 	if ( numsprites == 0 )
446 	{
447 		printlog("failed to identify any sprites in sprites.txt\n");
448 		return 6;
449 	}
450 	sprites = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*numsprites);
451 	fp = openDataFile("images/sprites.txt", "r");
452 	for ( c = 0; !feof(fp); c++ )
453 	{
454 		fscanf(fp, "%s", name);
455 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
456 			{
457 				break;
458 			}
459 		sprites[c] = loadImage(name);
460 		if ( sprites[c] == NULL )
461 		{
462 			printlog("warning: failed to load '%s' listed at line %d in sprites.txt\n", name, c + 1);
463 			if ( c == 0 )
464 			{
465 				printlog("sprite 0 cannot be NULL!\n");
466 				fclose(fp);
467 				return 7;
468 			}
469 		}
470 	}
471 	fclose(fp);
472 
473 	// print a loading message
474 	drawClearBuffers();
475 	TTF_SizeUTF8(ttf16, LOADSTR2, &w, &h);
476 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, LOADSTR2);
477 
478 	GO_SwapBuffers(screen);
479 
480 	// load models
481 	std::string modelsDirectory = PHYSFS_getRealDir("models/models.txt");
482 	modelsDirectory.append(PHYSFS_getDirSeparator()).append("models/models.txt");
483 	printlog("loading models from directory %s...\n", modelsDirectory.c_str());
484 
485 	fp = openDataFile(modelsDirectory.c_str(), "r");
486 	for ( nummodels = 0; !feof(fp); nummodels++ )
487 	{
488 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
489 			{
490 				break;
491 			}
492 	}
493 	fclose(fp);
494 	if ( nummodels == 0 )
495 	{
496 		printlog("failed to identify any models in models.txt\n");
497 		return 11;
498 	}
499 	models = (voxel_t**) malloc(sizeof(voxel_t*)*nummodels);
500 	fp = openDataFile(modelsDirectory.c_str(), "r");
501 	for ( c = 0; !feof(fp); c++ )
502 	{
503 		fscanf(fp, "%s", name);
504 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
505 			{
506 				break;
507 			}
508 		models[c] = loadVoxel(name);
509 		if ( models[c] == NULL )
510 		{
511 			printlog("warning: failed to load '%s' listed at line %d in models.txt\n", name, c + 1);
512 			if ( c == 0 )
513 			{
514 				printlog("model 0 cannot be NULL!\n");
515 				fclose(fp);
516 				return 12;
517 			}
518 		}
519 	}
520 	if ( !softwaremode )
521 	{
522 		generatePolyModels(0, nummodels, false);
523 	}
524 	fclose(fp);
525 	// print a loading message
526 	drawClearBuffers();
527 	TTF_SizeUTF8(ttf16, LOADSTR3, &w, &h);
528 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, LOADSTR3);
529 
530 	GO_SwapBuffers(screen);
531 
532 	// load tiles
533 	std::string tilesDirectory = PHYSFS_getRealDir("images/tiles.txt");
534 	tilesDirectory.append(PHYSFS_getDirSeparator()).append("images/tiles.txt");
535 	printlog("loading tiles from directory %s...\n", tilesDirectory.c_str());
536 
537 	fp = openDataFile(tilesDirectory.c_str(), "r");
538 	for ( numtiles = 0; !feof(fp); numtiles++ )
539 	{
540 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
541 			{
542 				break;
543 			}
544 	}
545 	fclose(fp);
546 	if ( numtiles == 0 )
547 	{
548 		printlog("failed to identify any tiles in tiles.txt\n");
549 		return 8;
550 	}
551 	tiles = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*numtiles);
552 	animatedtiles = (bool*) malloc(sizeof(bool) * numtiles);
553 	lavatiles = (bool*) malloc(sizeof(bool) * numtiles);
554 	swimmingtiles = (bool*)malloc(sizeof(bool) * numtiles);
555 	fp = openDataFile(tilesDirectory.c_str(), "r");
556 	for ( c = 0; !feof(fp); c++ )
557 	{
558 		fscanf(fp, "%s", name);
559 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
560 			{
561 				break;
562 			}
563 		tiles[c] = loadImage(name);
564 		animatedtiles[c] = false;
565 		lavatiles[c] = false;
566 		swimmingtiles[c] = false;
567 		if ( tiles[c] != NULL )
568 		{
569 			for (x = 0; x < strlen(name); x++)
570 			{
571 				if ( name[x] >= '0' && name[x] <= '9' )
572 				{
573 					// animated tiles if the tile name ends in a number 0-9.
574 					animatedtiles[c] = true;
575 					break;
576 				}
577 			}
578 			if ( strstr(name, "Lava") || strstr(name, "lava") )
579 			{
580 				lavatiles[c] = true;
581 			}
582 			if ( strstr(name, "Water") || strstr(name, "water") || strstr(name, "swimtile") || strstr(name, "Swimtile") )
583 			{
584 				swimmingtiles[c] = true;
585 			}
586 		}
587 		else
588 		{
589 			printlog("warning: failed to load '%s' listed at line %d in tiles.txt\n", name, c + 1);
590 			if ( c == 0 )
591 			{
592 				printlog("tile 0 cannot be NULL!\n");
593 				fclose(fp);
594 				return 9;
595 			}
596 		}
597 	}
598 	fclose(fp);
599 
600 	// print a loading message
601 	drawClearBuffers();
602 	TTF_SizeUTF8(ttf16, LOADSTR4, &w, &h);
603 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, LOADSTR4);
604 
605 	GO_SwapBuffers(screen);
606 
607 	// load sound effects
608 	std::string soundsDirectory = PHYSFS_getRealDir("sound/sounds.txt");
609 	soundsDirectory.append(PHYSFS_getDirSeparator()).append("sound/sounds.txt");
610 #ifdef USE_FMOD
611 	printlog("loading sounds...\n");
612 	fp = openDataFile(soundsDirectory.c_str(), "r");
613 	for ( numsounds = 0; !feof(fp); numsounds++ )
614 	{
615 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
616 			{
617 				break;
618 			}
619 	}
620 	fclose(fp);
621 	if ( numsounds == 0 )
622 	{
623 		printlog("failed to identify any sounds in sounds.txt\n");
624 		return 10;
625 	}
626 	sounds = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*numsounds);
627 	fp = openDataFile(soundsDirectory.c_str(), "r");
628 	for ( c = 0; !feof(fp); c++ )
629 	{
630 		fscanf(fp, "%s", name);
631 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
632 			{
633 				break;
634 			}
635 		//TODO: Might need to malloc the sounds[c]->sound
636 		fmod_result = FMOD_System_CreateSound(fmod_system, name, (FMOD_MODE)(FMOD_SOFTWARE | FMOD_3D), NULL, &sounds[c]);
637 		if (FMODErrorCheck())
638 		{
639 			printlog("warning: failed to load '%s' listed at line %d in sounds.txt\n", name, c + 1);
640 		}
641 		//TODO: set sound volume? Or otherwise handle sound volume.
642 	}
643 	fclose(fp);
644 	FMOD_ChannelGroup_SetVolume(sound_group, sfxvolume / 128.f);
645 	FMOD_ChannelGroup_SetVolume(soundAmbient_group, sfxAmbientVolume / 128.f);
646 	FMOD_ChannelGroup_SetVolume(soundEnvironment_group, sfxEnvironmentVolume / 128.f);
647 	FMOD_System_Set3DSettings(fmod_system, 1.0, 2.0, 1.0);
648 #elif defined USE_OPENAL
649 	printlog("loading sounds...\n");
650 	fp = openDataFile(soundsDirectory.c_str(), "r");
651 	for ( numsounds = 0; !feof(fp); numsounds++ )
652 	{
653 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
654 			{
655 				break;
656 			}
657 	}
658 	fclose(fp);
659 	if ( numsounds == 0 )
660 	{
661 		printlog("failed to identify any sounds in sounds.txt\n");
662 		return 10;
663 	}
664 	sounds = (OPENAL_BUFFER**) malloc(sizeof(OPENAL_BUFFER*)*numsounds);
665 	fp = openDataFile(soundsDirectory.c_str(), "r");
666 	for ( c = 0; !feof(fp); c++ )
667 	{
668 		fscanf(fp, "%s", name);
669 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
670 			{
671 				break;
672 			}
673 		//TODO: Might need to malloc the sounds[c]->sound
674 		OPENAL_CreateSound(name, true, &sounds[c]);
675 		//TODO: set sound volume? Or otherwise handle sound volume.
676 	}
677 	fclose(fp);
678 	OPENAL_ChannelGroup_SetVolume(sound_group, sfxvolume / 128.f);
679 	OPENAL_ChannelGroup_SetVolume(soundAmbient_group, sfxAmbientVolume / 128.f);
680 	OPENAL_ChannelGroup_SetVolume(soundEnvironment_group, sfxEnvironmentVolume / 128.f);
681 	//FMOD_System_Set3DSettings(fmod_system, 1.0, 2.0, 1.0); // This on is hardcoded, I've been lazy here'
682 #endif
683 	return 0;
684 }
685 
686 /*-------------------------------------------------------------------------------
687 
688 	loadLanguage
689 
690 	loads the language file with the given language code in *lang
691 
692 -------------------------------------------------------------------------------*/
693 
loadLanguage(char const * const lang)694 int loadLanguage(char const * const lang)
695 {
696 	char filename[128] = { 0 };
697 	FILE* fp;
698 	int c;
699 
700 	// open log file
701 	if ( !logfile )
702 	{
703 		openLogFile();
704 	}
705 
706 	// compose filename
707 	snprintf(filename, 127, "lang/%s.txt", lang);
708 	std::string langFilepath;
709 	if ( PHYSFS_isInit() && PHYSFS_getRealDir(filename) != NULL )
710 	{
711 		std::string langRealDir = PHYSFS_getRealDir(filename);
712 		langFilepath = langRealDir + PHYSFS_getDirSeparator() + filename;
713 	}
714 	else
715 	{
716 		langFilepath = filename;
717 	}
718 
719 	// check if language file is valid
720 	if ( !dataPathExists(langFilepath.c_str()) )
721 	{
722 		// language file doesn't exist
723 		printlog("error: unable to locate language file: '%s'", langFilepath.c_str());
724 		return 1;
725 	}
726 
727 	// check if we've loaded this language already
728 	if ( !strcmp(languageCode, lang) )
729 	{
730 		printlog("info: language '%s' already loaded", lang);
731 		return 1;
732 	}
733 
734 	// init SDL_TTF
735 	if ( !TTF_WasInit() )
736 	{
737 		if ( TTF_Init() == -1 )
738 		{
739 			printlog("failed to initialize SDL_ttf.\n");
740 			return 1;
741 		}
742 	}
743 
744 	// load fonts
745 	char fontName[64] = { 0 };
746 	char fontPath[1024];
747 	snprintf(fontName, 63, "lang/%s.ttf", lang);
748 	std::string fontFilepath;
749 	if ( PHYSFS_isInit() && PHYSFS_getRealDir(fontName) != NULL )
750 	{
751 		std::string fontRealDir = PHYSFS_getRealDir(fontName);
752 		fontFilepath = fontRealDir + PHYSFS_getDirSeparator() + fontName;
753 	}
754 	else
755 	{
756 		fontFilepath = fontName;
757 	}
758 
759 	if ( !dataPathExists(fontFilepath.c_str()) )
760 	{
761 		strncpy(fontName, "lang/en.ttf", 63);
762 		if ( PHYSFS_isInit() && PHYSFS_getRealDir(fontName) != NULL )
763 		{
764 			std::string fontRealDir = PHYSFS_getRealDir(fontName);
765 			fontFilepath = fontRealDir + PHYSFS_getDirSeparator() + fontName;
766 		}
767 		else
768 		{
769 			fontFilepath = fontName;
770 		}
771 	}
772 	if ( !dataPathExists(fontFilepath.c_str()) )
773 	{
774 		printlog("error: default game font 'lang/en.ttf' not found");
775 		return 1;
776 	}
777 	completePath(fontPath, fontFilepath.c_str());
778 	if ( ttf8 )
779 	{
780 		TTF_CloseFont(ttf8);
781 	}
782 	if ((ttf8 = TTF_OpenFont(fontPath, TTF8_HEIGHT)) == NULL )
783 	{
784 		printlog("failed to load size 8 ttf: %s\n", TTF_GetError());
785 		return 1;
786 	}
787 	TTF_SetFontKerning(ttf8, 0);
788 	TTF_SetFontHinting(ttf8, TTF_HINTING_MONO);
789 	if ( ttf12 )
790 	{
791 		TTF_CloseFont(ttf12);
792 	}
793 	if ((ttf12 = TTF_OpenFont(fontPath, TTF12_HEIGHT)) == NULL )
794 	{
795 		printlog("failed to load size 12 ttf: %s\n", TTF_GetError());
796 		return 1;
797 	}
798 	TTF_SetFontKerning(ttf12, 0);
799 	TTF_SetFontHinting(ttf12, TTF_HINTING_MONO);
800 	if ( ttf16 )
801 	{
802 		TTF_CloseFont(ttf16);
803 	}
804 	if ((ttf16 = TTF_OpenFont(fontPath, TTF16_HEIGHT)) == NULL )
805 	{
806 		printlog("failed to load size 16 ttf: %s\n", TTF_GetError());
807 		return 1;
808 	}
809 	TTF_SetFontKerning(ttf16, 0);
810 	TTF_SetFontHinting(ttf16, TTF_HINTING_MONO);
811 
812 	// open language file
813 	if ( (fp = openDataFile(langFilepath.c_str(), "r")) == NULL )
814 	{
815 		printlog("error: unable to load language file: '%s'", langFilepath.c_str());
816 		return 1;
817 	}
818 
819 	// free currently loaded language if any
820 	freeLanguages();
821 
822 	// store the new language code
823 	strcpy(languageCode, lang);
824 
825 	// allocate new language strings
826 	language = (char**) calloc(NUMLANGENTRIES, sizeof(char*));
827 
828 	// Allocate an emptry string for each possible language entry
829 	for (c = 0; c < NUMLANGENTRIES; c++)
830 	{
831 		language[c] = (char*)calloc(1, sizeof(char));
832 	}
833 
834 	// read file
835 	Uint32 line;
836 	for ( line = 1; !feof(fp); )
837 	{
838 		//printlog( "loading line %d...\n", line);
839 		char data[1024];
840 		int entry = NUMLANGENTRIES;
841 		int dummy;
842 
843 		// read line from file
844 		int i;
845 		bool fileEnd = false;
846 		for ( i = 0; ; i++ )
847 		{
848 			data[i] = fgetc(fp);
849 			if ( feof(fp) )
850 			{
851 				fileEnd = true;
852 				break;
853 			}
854 
855 			// blank or comment lines stop reading at a newline
856 			if ( data[i] == '\n' )
857 			{
858 				line++;
859 				if ( data[0] == '\n' || data[0] == '#' )
860 				{
861 					break;
862 				}
863 			}
864 			if ( data[i] == '#' )
865 			{
866 				if ( data[0] != '\n' && data[0] != '#' )
867 				{
868 					break;
869 				}
870 			}
871 		}
872 		if ( fileEnd )
873 		{
874 			break;
875 		}
876 
877 		// skip blank and comment lines
878 		if ( data[0] == '\n' || data[0] == '#' )
879 		{
880 			continue;
881 		}
882 
883 		data[i] = 0;
884 
885 		// process line
886 		if ( (entry = atoi(data)) == 0 )
887 		{
888 			printlog( "warning: syntax error in '%s':%d\n bad syntax!\n", langFilepath.c_str(), line);
889 			continue;
890 		}
891 		else if ( entry >= NUMLANGENTRIES || entry < 0 )
892 		{
893 			printlog( "warning: syntax error in '%s':%d\n invalid language entry!\n", langFilepath.c_str(), line);
894 			continue;
895 		}
896 		//printlog( "loading entry %d...\n", entry);
897 		char entryText[16] = { 0 };
898 		snprintf(entryText, 15, "%d", entry);
899 		if ( language[entry][0] )
900 		{
901 			printlog( "warning: duplicate entry %d in '%s':%d\n", entry, langFilepath.c_str(), line);
902 			free(language[entry]);
903 		}
904 		language[entry] = (char*) calloc(strlen((char*)(data + strlen(entryText) + 1)) + 1, sizeof(char));
905 		strcpy(language[entry], (char*)(data + strlen(entryText) + 1));
906 	}
907 
908 	// close file
909 	fclose(fp);
910 	printlog( "successfully loaded language file '%s'\n", langFilepath.c_str());
911 
912 	// update item internal language entries.
913 	for ( int c = 0; c < NUMITEMS; ++c )
914 	{
915 		if ( c > SPELLBOOK_DETECT_FOOD )
916 		{
917 			int newItems = c - SPELLBOOK_DETECT_FOOD - 1;
918 			items[c].name_identified = language[3500 + newItems * 2];
919 			items[c].name_unidentified = language[3501 + newItems * 2];
920 		}
921 		else if ( c > ARTIFACT_BOW )
922 		{
923 			int newItems = c - ARTIFACT_BOW - 1;
924 			items[c].name_identified = language[2200 + newItems * 2];
925 			items[c].name_unidentified = language[2201 + newItems * 2];
926 		}
927 		else
928 		{
929 			items[c].name_identified = language[1545 + c * 2];
930 			items[c].name_unidentified = language[1546 + c * 2];
931 		}
932 	}
933 	initMenuOptions();
934 	return 0;
935 }
936 
937 /*-------------------------------------------------------------------------------
938 
939 	reloadLanguage
940 
941 	reloads the current language file
942 
943 -------------------------------------------------------------------------------*/
944 
reloadLanguage()945 int reloadLanguage()
946 {
947 	char lang[32];
948 
949 	strcpy(lang, languageCode);
950 	strcpy(languageCode, "");
951 	return loadLanguage(lang);
952 }
953 
954 /*-------------------------------------------------------------------------------
955  *
956        freeLanguages
957 
958 	free languages string resources
959 
960 --------------------------------------------------------------------------------*/
961 
freeLanguages()962 void freeLanguages()
963 {
964 	int c;
965 
966 	if ( language )
967 	{
968 		for ( c = 0; c < NUMLANGENTRIES; c++ )
969 		{
970 			char* entry = language[c];
971 			if ( entry )
972 			{
973 				free(entry);
974 			}
975 		}
976 		free(language);
977 	}
978 }
979 
980 /*-------------------------------------------------------------------------------
981 
982 	generatePolyModels
983 
984 	processes voxel models and turns them into polygon-based models (surface
985 	optimized)
986 
987 -------------------------------------------------------------------------------*/
988 
generatePolyModels(int start,int end,bool forceCacheRebuild)989 void generatePolyModels(int start, int end, bool forceCacheRebuild)
990 {
991 	Sint32 x, y, z;
992 	Sint32 c, i;
993 	Uint32 index, indexdown[3];
994 	Uint8 newcolor, oldcolor;
995 	bool buildingquad;
996 	polyquad_t* quad1, *quad2;
997 	Uint32 numquads;
998 	list_t quads;
999 	FILE *model_cache;
1000 	bool generateAll = start == 0 && end == nummodels;
1001 
1002 	quads.first = NULL;
1003 	quads.last = NULL;
1004 
1005 	printlog("generating poly models...\n");
1006 	if ( generateAll )
1007 	{
1008 		polymodels = (polymodel_t*) malloc(sizeof(polymodel_t) * nummodels);
1009 		if ( useModelCache )
1010 		{
1011 			model_cache = openDataFile("models.cache", "rb");
1012 			if ( model_cache )
1013 			{
1014 				char polymodelsVersionStr[7] = "v0.0.0";
1015 				char modelsCacheHeader[7] = "000000";
1016 				fread(&modelsCacheHeader, sizeof(char), strlen("BARONY"), model_cache);
1017 
1018 				if ( !strcmp(modelsCacheHeader, "BARONY") )
1019 				{
1020 					// we're using the new polymodels file.
1021 					fread(&polymodelsVersionStr, sizeof(char), strlen(VERSION), model_cache);
1022 					printlog("[MODEL CACHE]: Using updated version format %s.", polymodelsVersionStr);
1023 					if ( strncmp(polymodelsVersionStr, VERSION, strlen(VERSION)) )
1024 					{
1025 						// different version.
1026 						forceCacheRebuild = true;
1027 						printlog("[MODEL CACHE]: Detected outdated version number %s - current is %s. Upgrading cache...", polymodelsVersionStr, VERSION);
1028 					}
1029 				}
1030 				else
1031 				{
1032 					printlog("[MODEL CACHE]: Detected legacy cache without embedded version data, upgrading cache to %s...", VERSION);
1033 					rewind(model_cache);
1034 					forceCacheRebuild = true; // upgrade from legacy cache
1035 				}
1036 				if ( !forceCacheRebuild )
1037 				{
1038 					for (size_t model_index = 0; model_index < nummodels; model_index++) {
1039 						polymodel_t *cur = &polymodels[model_index];
1040 						fread(&cur->numfaces, sizeof(cur->numfaces), 1, model_cache);
1041 						cur->faces = (polytriangle_t *) calloc(sizeof(polytriangle_t), cur->numfaces);
1042 						fread(polymodels[model_index].faces, sizeof(polytriangle_t), cur->numfaces, model_cache);
1043 					}
1044 					fclose(model_cache);
1045 					return generateVBOs(start, end);
1046 				}
1047 				else
1048 				{
1049 					fclose(model_cache);
1050 				}
1051 			}
1052 		}
1053 	}
1054 
1055 	for ( c = start; c < end; ++c )
1056 	{
1057 		char loadText[128];
1058 		snprintf(loadText, 127, language[745], c, nummodels);
1059 
1060 		// print a loading message
1061 		if ( start == 0 && end == nummodels )
1062 		{
1063 			if ( c % 50 == 0 )
1064 			{
1065 				drawClearBuffers();
1066 				int w, h;
1067 				TTF_SizeUTF8(ttf16, loadText, &w, &h);
1068 				ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, loadText);
1069 
1070 				GO_SwapBuffers(screen);
1071 			}
1072 		}
1073 		numquads = 0;
1074 		polymodels[c].numfaces = 0;
1075 		voxel_t* model = models[c];
1076 		if ( !model )
1077 		{
1078 			continue;
1079 		}
1080 		indexdown[0] = model->sizez * model->sizey;
1081 		indexdown[1] = model->sizez;
1082 		indexdown[2] = 1;
1083 
1084 		// find front faces
1085 		for ( x = models[c]->sizex - 1; x >= 0; x-- )
1086 		{
1087 			for ( z = 0; z < models[c]->sizez; z++ )
1088 			{
1089 				oldcolor = 255;
1090 				buildingquad = false;
1091 				for ( y = 0; y < models[c]->sizey; y++ )
1092 				{
1093 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1094 					newcolor = models[c]->data[index];
1095 					if ( buildingquad == true )
1096 					{
1097 						bool doit = false;
1098 						if ( newcolor != oldcolor )
1099 						{
1100 							doit = true;
1101 						}
1102 						else if ( x < models[c]->sizex - 1 )
1103 							if ( models[c]->data[index + indexdown[0]] >= 0 && models[c]->data[index + indexdown[0]] < 255 )
1104 							{
1105 								doit = true;
1106 							}
1107 						if ( doit )
1108 						{
1109 							// add the last two vertices to the previous quad
1110 							buildingquad = false;
1111 
1112 							node_t* currentNode = quads.last;
1113 							quad1 = (polyquad_t*)currentNode->element;
1114 							quad1->vertex[1].x = x - model->sizex / 2.f + 1;
1115 							quad1->vertex[1].y = y - model->sizey / 2.f;
1116 							quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1117 							quad1->vertex[2].x = x - model->sizex / 2.f + 1;
1118 							quad1->vertex[2].y = y - model->sizey / 2.f;
1119 							quad1->vertex[2].z = z - model->sizez / 2.f;
1120 
1121 							// optimize quad
1122 							node_t* node;
1123 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1124 							{
1125 								quad2 = (polyquad_t*)node->element;
1126 								if ( quad1->side == quad2->side )
1127 								{
1128 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1129 									{
1130 										if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1131 										{
1132 											if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1133 											{
1134 												quad2->vertex[2].z++;
1135 												quad2->vertex[3].z++;
1136 												list_RemoveNode(currentNode);
1137 												numquads--;
1138 												polymodels[c].numfaces -= 2;
1139 												break;
1140 											}
1141 										}
1142 									}
1143 								}
1144 							}
1145 						}
1146 					}
1147 					if ( newcolor != oldcolor || !buildingquad )
1148 					{
1149 						if ( newcolor != 255 )
1150 						{
1151 							bool doit = false;
1152 							if ( x == models[c]->sizex - 1 )
1153 							{
1154 								doit = true;
1155 							}
1156 							else if ( models[c]->data[index + indexdown[0]] == 255 )
1157 							{
1158 								doit = true;
1159 							}
1160 							if ( doit )
1161 							{
1162 								// start building a new quad
1163 								buildingquad = true;
1164 								numquads++;
1165 								polymodels[c].numfaces += 2;
1166 
1167 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1168 								quad1->side = 0;
1169 								quad1->vertex[0].x = x - model->sizex / 2.f + 1;
1170 								quad1->vertex[0].y = y - model->sizey / 2.f;
1171 								quad1->vertex[0].z = z - model->sizez / 2.f - 1;
1172 								quad1->vertex[3].x = x - model->sizex / 2.f + 1;
1173 								quad1->vertex[3].y = y - model->sizey / 2.f;
1174 								quad1->vertex[3].z = z - model->sizez / 2.f;
1175 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1176 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1177 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1178 
1179 								node_t* newNode = list_AddNodeLast(&quads);
1180 								newNode->element = quad1;
1181 								newNode->deconstructor = &defaultDeconstructor;
1182 								newNode->size = sizeof(polyquad_t);
1183 							}
1184 						}
1185 					}
1186 					oldcolor = newcolor;
1187 				}
1188 				if ( buildingquad == true )
1189 				{
1190 					// add the last two vertices to the previous quad
1191 					buildingquad = false;
1192 
1193 					node_t* currentNode = quads.last;
1194 					quad1 = (polyquad_t*)currentNode->element;
1195 					quad1->vertex[1].x = x - model->sizex / 2.f + 1;
1196 					quad1->vertex[1].y = y - model->sizey / 2.f;
1197 					quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1198 					quad1->vertex[2].x = x - model->sizex / 2.f + 1;
1199 					quad1->vertex[2].y = y - model->sizey / 2.f;
1200 					quad1->vertex[2].z = z - model->sizez / 2.f;
1201 
1202 					// optimize quad
1203 					node_t* node;
1204 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1205 					{
1206 						quad2 = (polyquad_t*)node->element;
1207 						if ( quad1->side == quad2->side )
1208 						{
1209 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1210 							{
1211 								if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1212 								{
1213 									if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1214 									{
1215 										quad2->vertex[2].z++;
1216 										quad2->vertex[3].z++;
1217 										list_RemoveNode(currentNode);
1218 										numquads--;
1219 										polymodels[c].numfaces -= 2;
1220 										break;
1221 									}
1222 								}
1223 							}
1224 						}
1225 					}
1226 				}
1227 			}
1228 		}
1229 
1230 		// find back faces
1231 		for ( x = 0; x < models[c]->sizex; x++ )
1232 		{
1233 			for ( z = 0; z < models[c]->sizez; z++ )
1234 			{
1235 				oldcolor = 255;
1236 				buildingquad = false;
1237 				for ( y = 0; y < models[c]->sizey; y++ )
1238 				{
1239 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1240 					newcolor = models[c]->data[index];
1241 					if ( buildingquad == true )
1242 					{
1243 						bool doit = false;
1244 						if ( newcolor != oldcolor )
1245 						{
1246 							doit = true;
1247 						}
1248 						else if ( x > 0 )
1249 							if ( models[c]->data[index - indexdown[0]] >= 0 && models[c]->data[index - indexdown[0]] < 255 )
1250 							{
1251 								doit = true;
1252 							}
1253 						if ( doit )
1254 						{
1255 							// add the last two vertices to the previous quad
1256 							buildingquad = false;
1257 
1258 							node_t* currentNode = quads.last;
1259 							quad1 = (polyquad_t*)currentNode->element;
1260 							quad1->vertex[1].x = x - model->sizex / 2.f;
1261 							quad1->vertex[1].y = y - model->sizey / 2.f;
1262 							quad1->vertex[1].z = z - model->sizez / 2.f;
1263 							quad1->vertex[2].x = x - model->sizex / 2.f;
1264 							quad1->vertex[2].y = y - model->sizey / 2.f;
1265 							quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1266 
1267 							// optimize quad
1268 							node_t* node;
1269 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1270 							{
1271 								quad2 = (polyquad_t*)node->element;
1272 								if ( quad1->side == quad2->side )
1273 								{
1274 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1275 									{
1276 										if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1277 										{
1278 											if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1279 											{
1280 												quad2->vertex[0].z++;
1281 												quad2->vertex[1].z++;
1282 												list_RemoveNode(currentNode);
1283 												numquads--;
1284 												polymodels[c].numfaces -= 2;
1285 												break;
1286 											}
1287 										}
1288 									}
1289 								}
1290 							}
1291 						}
1292 					}
1293 					if ( newcolor != oldcolor || !buildingquad )
1294 					{
1295 						if ( newcolor != 255 )
1296 						{
1297 							bool doit = false;
1298 							if ( x == 0 )
1299 							{
1300 								doit = true;
1301 							}
1302 							else if ( models[c]->data[index - indexdown[0]] == 255 )
1303 							{
1304 								doit = true;
1305 							}
1306 							if ( doit )
1307 							{
1308 								// start building a new quad
1309 								buildingquad = true;
1310 								numquads++;
1311 								polymodels[c].numfaces += 2;
1312 
1313 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1314 								quad1->side = 1;
1315 								quad1->vertex[0].x = x - model->sizex / 2.f;
1316 								quad1->vertex[0].y = y - model->sizey / 2.f;
1317 								quad1->vertex[0].z = z - model->sizez / 2.f;
1318 								quad1->vertex[3].x = x - model->sizex / 2.f;
1319 								quad1->vertex[3].y = y - model->sizey / 2.f;
1320 								quad1->vertex[3].z = z - model->sizez / 2.f - 1;
1321 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1322 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1323 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1324 
1325 								node_t* newNode = list_AddNodeLast(&quads);
1326 								newNode->element = quad1;
1327 								newNode->deconstructor = &defaultDeconstructor;
1328 								newNode->size = sizeof(polyquad_t);
1329 							}
1330 						}
1331 					}
1332 					oldcolor = newcolor;
1333 				}
1334 				if ( buildingquad == true )
1335 				{
1336 					// add the last two vertices to the previous quad
1337 					buildingquad = false;
1338 
1339 					node_t* currentNode = quads.last;
1340 					quad1 = (polyquad_t*)currentNode->element;
1341 					quad1->vertex[1].x = x - model->sizex / 2.f;
1342 					quad1->vertex[1].y = y - model->sizey / 2.f;
1343 					quad1->vertex[1].z = z - model->sizez / 2.f;
1344 					quad1->vertex[2].x = x - model->sizex / 2.f;
1345 					quad1->vertex[2].y = y - model->sizey / 2.f;
1346 					quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1347 
1348 					// optimize quad
1349 					node_t* node;
1350 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1351 					{
1352 						quad2 = (polyquad_t*)node->element;
1353 						if ( quad1->side == quad2->side )
1354 						{
1355 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1356 							{
1357 								if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1358 								{
1359 									if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1360 									{
1361 										quad2->vertex[0].z++;
1362 										quad2->vertex[1].z++;
1363 										list_RemoveNode(currentNode);
1364 										numquads--;
1365 										polymodels[c].numfaces -= 2;
1366 										break;
1367 									}
1368 								}
1369 							}
1370 						}
1371 					}
1372 				}
1373 			}
1374 		}
1375 
1376 		// find right faces
1377 		for ( y = models[c]->sizey - 1; y >= 0; y-- )
1378 		{
1379 			for ( z = 0; z < models[c]->sizez; z++ )
1380 			{
1381 				oldcolor = 255;
1382 				buildingquad = false;
1383 				for ( x = 0; x < models[c]->sizex; x++ )
1384 				{
1385 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1386 					newcolor = models[c]->data[index];
1387 					if ( buildingquad == true )
1388 					{
1389 						bool doit = false;
1390 						if ( newcolor != oldcolor )
1391 						{
1392 							doit = true;
1393 						}
1394 						else if ( y < models[c]->sizey - 1 )
1395 							if ( models[c]->data[index + indexdown[1]] >= 0 && models[c]->data[index + indexdown[1]] < 255 )
1396 							{
1397 								doit = true;
1398 							}
1399 						if ( doit )
1400 						{
1401 							// add the last two vertices to the previous quad
1402 							buildingquad = false;
1403 
1404 							node_t* currentNode = quads.last;
1405 							quad1 = (polyquad_t*) currentNode->element;
1406 							quad1->vertex[1].x = x - model->sizex / 2.f;
1407 							quad1->vertex[1].y = y - model->sizey / 2.f + 1;
1408 							quad1->vertex[1].z = z - model->sizez / 2.f;
1409 							quad1->vertex[2].x = x - model->sizex / 2.f;
1410 							quad1->vertex[2].y = y - model->sizey / 2.f + 1;
1411 							quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1412 
1413 							// optimize quad
1414 							node_t* node;
1415 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1416 							{
1417 								quad2 = (polyquad_t*)node->element;
1418 								if ( quad1->side == quad2->side )
1419 								{
1420 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1421 									{
1422 										if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1423 										{
1424 											if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1425 											{
1426 												quad2->vertex[0].z++;
1427 												quad2->vertex[1].z++;
1428 												list_RemoveNode(currentNode);
1429 												numquads--;
1430 												polymodels[c].numfaces -= 2;
1431 												break;
1432 											}
1433 										}
1434 									}
1435 								}
1436 							}
1437 						}
1438 					}
1439 					if ( newcolor != oldcolor || !buildingquad )
1440 					{
1441 						if ( newcolor != 255 )
1442 						{
1443 							bool doit = false;
1444 							if ( y == models[c]->sizey - 1 )
1445 							{
1446 								doit = true;
1447 							}
1448 							else if ( models[c]->data[index + indexdown[1]] == 255 )
1449 							{
1450 								doit = true;
1451 							}
1452 							if ( doit )
1453 							{
1454 								// start building a new quad
1455 								buildingquad = true;
1456 								numquads++;
1457 								polymodels[c].numfaces += 2;
1458 
1459 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1460 								quad1->side = 2;
1461 								quad1->vertex[0].x = x - model->sizex / 2.f;
1462 								quad1->vertex[0].y = y - model->sizey / 2.f + 1;
1463 								quad1->vertex[0].z = z - model->sizez / 2.f;
1464 								quad1->vertex[3].x = x - model->sizex / 2.f;
1465 								quad1->vertex[3].y = y - model->sizey / 2.f + 1;
1466 								quad1->vertex[3].z = z - model->sizez / 2.f - 1;
1467 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1468 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1469 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1470 
1471 								node_t* newNode = list_AddNodeLast(&quads);
1472 								newNode->element = quad1;
1473 								newNode->deconstructor = &defaultDeconstructor;
1474 								newNode->size = sizeof(polyquad_t);
1475 							}
1476 						}
1477 					}
1478 					oldcolor = newcolor;
1479 				}
1480 				if ( buildingquad == true )
1481 				{
1482 					// add the last two vertices to the previous quad
1483 					buildingquad = false;
1484 					node_t* currentNode = quads.last;
1485 					quad1 = (polyquad_t*) currentNode->element;
1486 					quad1->vertex[1].x = x - model->sizex / 2.f;
1487 					quad1->vertex[1].y = y - model->sizey / 2.f + 1;
1488 					quad1->vertex[1].z = z - model->sizez / 2.f;
1489 					quad1->vertex[2].x = x - model->sizex / 2.f;
1490 					quad1->vertex[2].y = y - model->sizey / 2.f + 1;
1491 					quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1492 
1493 					// optimize quad
1494 					node_t* node;
1495 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1496 					{
1497 						quad2 = (polyquad_t*)node->element;
1498 						if ( quad1->side == quad2->side )
1499 						{
1500 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1501 							{
1502 								if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1503 								{
1504 									if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1505 									{
1506 										quad2->vertex[0].z++;
1507 										quad2->vertex[1].z++;
1508 										list_RemoveNode(currentNode);
1509 										numquads--;
1510 										polymodels[c].numfaces -= 2;
1511 										break;
1512 									}
1513 								}
1514 							}
1515 						}
1516 					}
1517 				}
1518 			}
1519 		}
1520 
1521 		// find left faces
1522 		for ( y = 0; y < models[c]->sizey; y++ )
1523 		{
1524 			for ( z = 0; z < models[c]->sizez; z++ )
1525 			{
1526 				oldcolor = 255;
1527 				buildingquad = false;
1528 				for ( x = 0; x < models[c]->sizex; x++ )
1529 				{
1530 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1531 					newcolor = models[c]->data[index];
1532 					if ( buildingquad == true )
1533 					{
1534 						bool doit = false;
1535 						if ( newcolor != oldcolor )
1536 						{
1537 							doit = true;
1538 						}
1539 						else if ( y > 0 )
1540 							if ( models[c]->data[index - indexdown[1]] >= 0 && models[c]->data[index - indexdown[1]] < 255 )
1541 							{
1542 								doit = true;
1543 							}
1544 						if ( doit )
1545 						{
1546 							// add the last two vertices to the previous quad
1547 							buildingquad = false;
1548 
1549 							node_t* currentNode = quads.last;
1550 							quad1 = (polyquad_t*) currentNode->element;
1551 							quad1->vertex[1].x = x - model->sizex / 2.f;
1552 							quad1->vertex[1].y = y - model->sizey / 2.f;
1553 							quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1554 							quad1->vertex[2].x = x - model->sizex / 2.f;
1555 							quad1->vertex[2].y = y - model->sizey / 2.f;
1556 							quad1->vertex[2].z = z - model->sizez / 2.f;
1557 
1558 							// optimize quad
1559 							node_t* node;
1560 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1561 							{
1562 								quad2 = (polyquad_t*)node->element;
1563 								if ( quad1->side == quad2->side )
1564 								{
1565 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1566 									{
1567 										if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1568 										{
1569 											if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1570 											{
1571 												quad2->vertex[2].z++;
1572 												quad2->vertex[3].z++;
1573 												list_RemoveNode(currentNode);
1574 												numquads--;
1575 												polymodels[c].numfaces -= 2;
1576 												break;
1577 											}
1578 										}
1579 									}
1580 								}
1581 							}
1582 						}
1583 					}
1584 					if ( newcolor != oldcolor || !buildingquad )
1585 					{
1586 						if ( newcolor != 255 )
1587 						{
1588 							bool doit = false;
1589 							if ( y == 0 )
1590 							{
1591 								doit = true;
1592 							}
1593 							else if ( models[c]->data[index - indexdown[1]] == 255 )
1594 							{
1595 								doit = true;
1596 							}
1597 							if ( doit )
1598 							{
1599 								// start building a new quad
1600 								buildingquad = true;
1601 								numquads++;
1602 								polymodels[c].numfaces += 2;
1603 
1604 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1605 								quad1->side = 3;
1606 								quad1->vertex[0].x = x - model->sizex / 2.f;
1607 								quad1->vertex[0].y = y - model->sizey / 2.f;
1608 								quad1->vertex[0].z = z - model->sizez / 2.f - 1;
1609 								quad1->vertex[3].x = x - model->sizex / 2.f;
1610 								quad1->vertex[3].y = y - model->sizey / 2.f;
1611 								quad1->vertex[3].z = z - model->sizez / 2.f;
1612 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1613 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1614 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1615 
1616 								node_t* newNode = list_AddNodeLast(&quads);
1617 								newNode->element = quad1;
1618 								newNode->deconstructor = &defaultDeconstructor;
1619 								newNode->size = sizeof(polyquad_t);
1620 							}
1621 						}
1622 					}
1623 					oldcolor = newcolor;
1624 				}
1625 				if ( buildingquad == true )
1626 				{
1627 					// add the last two vertices to the previous quad
1628 					buildingquad = false;
1629 					node_t* currentNode = quads.last;
1630 					quad1 = (polyquad_t*) currentNode->element;
1631 					quad1->vertex[1].x = x - model->sizex / 2.f;
1632 					quad1->vertex[1].y = y - model->sizey / 2.f;
1633 					quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1634 					quad1->vertex[2].x = x - model->sizex / 2.f;
1635 					quad1->vertex[2].y = y - model->sizey / 2.f;
1636 					quad1->vertex[2].z = z - model->sizez / 2.f;
1637 
1638 					// optimize quad
1639 					node_t* node;
1640 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1641 					{
1642 						quad2 = (polyquad_t*)node->element;
1643 						if ( quad1->side == quad2->side )
1644 						{
1645 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1646 							{
1647 								if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1648 								{
1649 									if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1650 									{
1651 										quad2->vertex[2].z++;
1652 										quad2->vertex[3].z++;
1653 										list_RemoveNode(currentNode);
1654 										numquads--;
1655 										polymodels[c].numfaces -= 2;
1656 										break;
1657 									}
1658 								}
1659 							}
1660 						}
1661 					}
1662 				}
1663 			}
1664 		}
1665 
1666 		// find bottom faces
1667 		for ( z = models[c]->sizez - 1; z >= 0; z-- )
1668 		{
1669 			for ( y = 0; y < models[c]->sizey; y++ )
1670 			{
1671 				oldcolor = 255;
1672 				buildingquad = false;
1673 				for ( x = 0; x < models[c]->sizex; x++ )
1674 				{
1675 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1676 					newcolor = models[c]->data[index];
1677 					if ( buildingquad == true )
1678 					{
1679 						bool doit = false;
1680 						if ( newcolor != oldcolor )
1681 						{
1682 							doit = true;
1683 						}
1684 						else if ( z < models[c]->sizez - 1 )
1685 							if ( models[c]->data[index + indexdown[2]] >= 0 && models[c]->data[index + indexdown[2]] < 255 )
1686 							{
1687 								doit = true;
1688 							}
1689 						if ( doit )
1690 						{
1691 							// add the last two vertices to the previous quad
1692 							buildingquad = false;
1693 
1694 							node_t* currentNode = quads.last;
1695 							quad1 = (polyquad_t*) currentNode->element;
1696 							quad1->vertex[1].x = x - model->sizex / 2.f;
1697 							quad1->vertex[1].y = y - model->sizey / 2.f;
1698 							quad1->vertex[1].z = z - model->sizez / 2.f;
1699 							quad1->vertex[2].x = x - model->sizex / 2.f;
1700 							quad1->vertex[2].y = y - model->sizey / 2.f + 1;
1701 							quad1->vertex[2].z = z - model->sizez / 2.f;
1702 
1703 							// optimize quad
1704 							node_t* node;
1705 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1706 							{
1707 								quad2 = (polyquad_t*)node->element;
1708 								if ( quad1->side == quad2->side )
1709 								{
1710 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1711 									{
1712 										if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1713 										{
1714 											if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1715 											{
1716 												quad2->vertex[2].y++;
1717 												quad2->vertex[3].y++;
1718 												list_RemoveNode(currentNode);
1719 												numquads--;
1720 												polymodels[c].numfaces -= 2;
1721 												break;
1722 											}
1723 										}
1724 									}
1725 								}
1726 							}
1727 						}
1728 					}
1729 					if ( newcolor != oldcolor || !buildingquad )
1730 					{
1731 						if ( newcolor != 255 )
1732 						{
1733 							bool doit = false;
1734 							if ( z == models[c]->sizez - 1 )
1735 							{
1736 								doit = true;
1737 							}
1738 							else if ( models[c]->data[index + indexdown[2]] == 255 )
1739 							{
1740 								doit = true;
1741 							}
1742 							if ( doit )
1743 							{
1744 								// start building a new quad
1745 								buildingquad = true;
1746 								numquads++;
1747 								polymodels[c].numfaces += 2;
1748 
1749 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1750 								quad1->side = 4;
1751 								quad1->vertex[0].x = x - model->sizex / 2.f;
1752 								quad1->vertex[0].y = y - model->sizey / 2.f;
1753 								quad1->vertex[0].z = z - model->sizez / 2.f;
1754 								quad1->vertex[3].x = x - model->sizex / 2.f;
1755 								quad1->vertex[3].y = y - model->sizey / 2.f + 1;
1756 								quad1->vertex[3].z = z - model->sizez / 2.f;
1757 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1758 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1759 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1760 
1761 								node_t* newNode = list_AddNodeLast(&quads);
1762 								newNode->element = quad1;
1763 								newNode->deconstructor = &defaultDeconstructor;
1764 								newNode->size = sizeof(polyquad_t);
1765 							}
1766 						}
1767 					}
1768 					oldcolor = newcolor;
1769 				}
1770 				if ( buildingquad == true )
1771 				{
1772 					// add the last two vertices to the previous quad
1773 					buildingquad = false;
1774 
1775 					node_t* currentNode = quads.last;
1776 					quad1 = (polyquad_t*) currentNode->element;
1777 					quad1->vertex[1].x = x - model->sizex / 2.f;
1778 					quad1->vertex[1].y = y - model->sizey / 2.f;
1779 					quad1->vertex[1].z = z - model->sizez / 2.f;
1780 					quad1->vertex[2].x = x - model->sizex / 2.f;
1781 					quad1->vertex[2].y = y - model->sizey / 2.f + 1;
1782 					quad1->vertex[2].z = z - model->sizez / 2.f;
1783 
1784 					// optimize quad
1785 					node_t* node;
1786 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1787 					{
1788 						quad2 = (polyquad_t*)node->element;
1789 						if ( quad1->side == quad2->side )
1790 						{
1791 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1792 							{
1793 								if ( quad2->vertex[3].x == quad1->vertex[0].x && quad2->vertex[3].y == quad1->vertex[0].y && quad2->vertex[3].z == quad1->vertex[0].z )
1794 								{
1795 									if ( quad2->vertex[2].x == quad1->vertex[1].x && quad2->vertex[2].y == quad1->vertex[1].y && quad2->vertex[2].z == quad1->vertex[1].z )
1796 									{
1797 										quad2->vertex[2].y++;
1798 										quad2->vertex[3].y++;
1799 										list_RemoveNode(currentNode);
1800 										numquads--;
1801 										polymodels[c].numfaces -= 2;
1802 										break;
1803 									}
1804 								}
1805 							}
1806 						}
1807 					}
1808 				}
1809 			}
1810 		}
1811 
1812 		// find top faces
1813 		for ( z = 0; z < models[c]->sizez; z++ )
1814 		{
1815 			for ( y = 0; y < models[c]->sizey; y++ )
1816 			{
1817 				oldcolor = 255;
1818 				buildingquad = false;
1819 				for ( x = 0; x < models[c]->sizex; x++ )
1820 				{
1821 					index = z + y * models[c]->sizez + x * models[c]->sizey * models[c]->sizez;
1822 					newcolor = models[c]->data[index];
1823 					if ( buildingquad == true )
1824 					{
1825 						bool doit = false;
1826 						if ( newcolor != oldcolor )
1827 						{
1828 							doit = true;
1829 						}
1830 						else if ( z > 0 )
1831 							if ( models[c]->data[index - indexdown[2]] >= 0 && models[c]->data[index - indexdown[2]] < 255 )
1832 							{
1833 								doit = true;
1834 							}
1835 						if ( doit )
1836 						{
1837 							// add the last two vertices to the previous quad
1838 							buildingquad = false;
1839 
1840 							node_t* currentNode = quads.last;
1841 							quad1 = (polyquad_t*) currentNode->element;
1842 							quad1->vertex[1].x = x - model->sizex / 2.f;
1843 							quad1->vertex[1].y = y - model->sizey / 2.f + 1;
1844 							quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1845 							quad1->vertex[2].x = x - model->sizex / 2.f;
1846 							quad1->vertex[2].y = y - model->sizey / 2.f;
1847 							quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1848 
1849 							// optimize quad
1850 							node_t* node;
1851 							for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1852 							{
1853 								quad2 = (polyquad_t*)node->element;
1854 								if ( quad1->side == quad2->side )
1855 								{
1856 									if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1857 									{
1858 										if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1859 										{
1860 											if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1861 											{
1862 												quad2->vertex[0].y++;
1863 												quad2->vertex[1].y++;
1864 												list_RemoveNode(currentNode);
1865 												numquads--;
1866 												polymodels[c].numfaces -= 2;
1867 												break;
1868 											}
1869 										}
1870 									}
1871 								}
1872 							}
1873 						}
1874 					}
1875 					if ( newcolor != oldcolor || !buildingquad )
1876 					{
1877 						if ( newcolor != 255 )
1878 						{
1879 							bool doit = false;
1880 							if ( z == 0 )
1881 							{
1882 								doit = true;
1883 							}
1884 							else if ( models[c]->data[index - indexdown[2]] == 255 )
1885 							{
1886 								doit = true;
1887 							}
1888 							if ( doit )
1889 							{
1890 								// start building a new quad
1891 								buildingquad = true;
1892 								numquads++;
1893 								polymodels[c].numfaces += 2;
1894 
1895 								quad1 = (polyquad_t*) calloc(1, sizeof(polyquad_t));
1896 								quad1->side = 5;
1897 								quad1->vertex[0].x = x - model->sizex / 2.f;
1898 								quad1->vertex[0].y = y - model->sizey / 2.f + 1;
1899 								quad1->vertex[0].z = z - model->sizez / 2.f - 1;
1900 								quad1->vertex[3].x = x - model->sizex / 2.f;
1901 								quad1->vertex[3].y = y - model->sizey / 2.f;
1902 								quad1->vertex[3].z = z - model->sizez / 2.f - 1;
1903 								quad1->r = models[c]->palette[models[c]->data[index]][0];
1904 								quad1->g = models[c]->palette[models[c]->data[index]][1];
1905 								quad1->b = models[c]->palette[models[c]->data[index]][2];
1906 
1907 								node_t* newNode = list_AddNodeLast(&quads);
1908 								newNode->element = quad1;
1909 								newNode->deconstructor = &defaultDeconstructor;
1910 								newNode->size = sizeof(polyquad_t);
1911 							}
1912 						}
1913 					}
1914 					oldcolor = newcolor;
1915 				}
1916 				if ( buildingquad == true )
1917 				{
1918 					// add the last two vertices to the previous quad
1919 					buildingquad = false;
1920 
1921 					node_t* currentNode = quads.last;
1922 					quad1 = (polyquad_t*) currentNode->element;
1923 					quad1->vertex[1].x = x - model->sizex / 2.f;
1924 					quad1->vertex[1].y = y - model->sizey / 2.f + 1;
1925 					quad1->vertex[1].z = z - model->sizez / 2.f - 1;
1926 					quad1->vertex[2].x = x - model->sizex / 2.f;
1927 					quad1->vertex[2].y = y - model->sizey / 2.f;
1928 					quad1->vertex[2].z = z - model->sizez / 2.f - 1;
1929 
1930 					// optimize quad
1931 					node_t* node;
1932 					for ( i = 0, node = quads.first; i < numquads - 1; i++, node = node->next )
1933 					{
1934 						quad2 = (polyquad_t*)node->element;
1935 						if ( quad1->side == quad2->side )
1936 						{
1937 							if ( quad1->r == quad2->r && quad1->g == quad2->g && quad1->b == quad2->b )
1938 							{
1939 								if ( quad2->vertex[0].x == quad1->vertex[3].x && quad2->vertex[0].y == quad1->vertex[3].y && quad2->vertex[0].z == quad1->vertex[3].z )
1940 								{
1941 									if ( quad2->vertex[1].x == quad1->vertex[2].x && quad2->vertex[1].y == quad1->vertex[2].y && quad2->vertex[1].z == quad1->vertex[2].z )
1942 									{
1943 										quad2->vertex[0].y++;
1944 										quad2->vertex[1].y++;
1945 										list_RemoveNode(currentNode);
1946 										numquads--;
1947 										polymodels[c].numfaces -= 2;
1948 										break;
1949 									}
1950 								}
1951 							}
1952 						}
1953 					}
1954 				}
1955 			}
1956 		}
1957 
1958 		// translate quads into triangles
1959 		polymodels[c].faces = (polytriangle_t*) malloc(sizeof(polytriangle_t) * polymodels[c].numfaces);
1960 		for ( i = 0; i < polymodels[c].numfaces; i++ )
1961 		{
1962 			node_t* node = list_Node(&quads, i / 2);
1963 			polyquad_t* quad = (polyquad_t*)node->element;
1964 			polymodels[c].faces[i].r = quad->r;
1965 			polymodels[c].faces[i].g = quad->g;
1966 			polymodels[c].faces[i].b = quad->b;
1967 			if ( i % 2 )
1968 			{
1969 				polymodels[c].faces[i].vertex[0] = quad->vertex[0];
1970 				polymodels[c].faces[i].vertex[1] = quad->vertex[1];
1971 				polymodels[c].faces[i].vertex[2] = quad->vertex[2];
1972 			}
1973 			else
1974 			{
1975 				polymodels[c].faces[i].vertex[0] = quad->vertex[0];
1976 				polymodels[c].faces[i].vertex[1] = quad->vertex[2];
1977 				polymodels[c].faces[i].vertex[2] = quad->vertex[3];
1978 			}
1979 		}
1980 
1981 		// free up quads for the next model
1982 		list_FreeAll(&quads);
1983 	}
1984 	if (useModelCache && (model_cache = openDataFile("models.cache", "wb")))
1985 	{
1986 		char modelCacheHeader[32] = "BARONY";
1987 		strcat(modelCacheHeader, VERSION);
1988 		fwrite(&modelCacheHeader, sizeof(char), strlen(modelCacheHeader), model_cache);
1989 		for (size_t model_index = 0; model_index < nummodels; model_index++)
1990 		{
1991 			polymodel_t *cur = &polymodels[model_index];
1992 			fwrite(&cur->numfaces, sizeof(cur->numfaces), 1, model_cache);
1993 			fwrite(cur->faces, sizeof(polytriangle_t), cur->numfaces, model_cache);
1994 		}
1995 		fclose(model_cache);
1996 	}
1997 
1998 	// now store models into VBOs
1999 	if ( !disablevbos )
2000 	{
2001 		generateVBOs(start, end);
2002 	}
2003 }
2004 
2005 /*-------------------------------------------------------------------------------
2006 
2007 	generateVBOs
2008 
2009 	generates VBOs/VAs from polymodel data
2010 
2011 -------------------------------------------------------------------------------*/
2012 
generateVBOs(int start,int end)2013 void generateVBOs(int start, int end)
2014 {
2015 	int count = end - start;
2016 
2017 	std::unique_ptr<GLuint[]> vas(new GLuint[count]);
2018 	SDL_glGenVertexArrays(count, vas.get());
2019 
2020 	std::unique_ptr<GLuint[]> vbos(new GLuint[count]);
2021 	SDL_glGenBuffers(count, vbos.get());
2022 
2023 	std::unique_ptr<GLuint[]> color_buffers(new GLuint[count]);
2024 	SDL_glGenBuffers(count, color_buffers.get());
2025 
2026 	std::unique_ptr<GLuint[]> color_shifted_buffers(new GLuint[count]);
2027 	SDL_glGenBuffers(count, color_shifted_buffers.get());
2028 
2029 	for ( int c = start; c < end; ++c )
2030 	{
2031 		polymodel_t *model = &polymodels[c];
2032 		std::unique_ptr<GLfloat[]> points(new GLfloat[9 * model->numfaces]);
2033 		std::unique_ptr<GLfloat[]> colors(new GLfloat[9 * model->numfaces]);
2034 		std::unique_ptr<GLfloat[]> colors_shifted(new GLfloat[9 * model->numfaces]);
2035 		for ( int i = 0; i < model->numfaces; i++ )
2036 		{
2037 			const polytriangle_t *face = &model->faces[i];
2038 			for (int vert_index = 0; vert_index < 3; vert_index++)
2039 			{
2040 				int data_index = i * 9 + vert_index * 3;
2041 				const vertex_t *vert = &face->vertex[vert_index];
2042 
2043 				points[data_index] = vert->x;
2044 				points[data_index + 1] = -vert->z;
2045 				points[data_index + 2] = vert->y;
2046 
2047 				colors[data_index] = face->r / 255.f;
2048 				colors[data_index + 1] = face->g / 255.f;
2049 				colors[data_index + 2] = face->b / 255.f;
2050 
2051 				colors_shifted[data_index] = face->b / 255.f;
2052 				colors_shifted[data_index + 1] = face->r / 255.f;
2053 				colors_shifted[data_index + 2] = face->g / 255.f;
2054 			}
2055 		}
2056 		model->va = vas[c - start];
2057 		model->vbo = vbos[c - start];
2058 		model->colors = color_buffers[c - start];
2059 		model->colors_shifted = color_shifted_buffers[c - start];
2060 		SDL_glBindVertexArray(model->va);
2061 
2062 		// vertex data
2063 		// Well, the generic vertex array are not used, so disabled (making it run on any OpenGL 1.5 hardware)
2064 		SDL_glBindBuffer(GL_ARRAY_BUFFER, model->vbo);
2065 		SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, points.get(), GL_STATIC_DRAW);
2066 
2067 		// color data
2068 		SDL_glBindBuffer(GL_ARRAY_BUFFER, model->colors);
2069 		SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, colors.get(), GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW
2070 
2071 		// shifted color data
2072 		SDL_glBindBuffer(GL_ARRAY_BUFFER, model->colors_shifted);
2073 		SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, colors_shifted.get(), GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW
2074 	}
2075 }
2076 
2077 /*-------------------------------------------------------------------------------
2078 
2079 	deinitApp
2080 
2081 	frees all memory consumed by the application and terminates the engine
2082 
2083 -------------------------------------------------------------------------------*/
deinitApp()2084 int deinitApp()
2085 {
2086 	Uint32 c;
2087 #ifdef USE_OPENAL
2088 	closeOPENAL();
2089 #endif
2090 	// close engine
2091 	printlog("closing engine...\n");
2092 	printlog("removing engine timer...\n");
2093 	if ( timer )
2094 	{
2095 		SDL_RemoveTimer(timer);
2096 	}
2097 	printlog("freeing engine resources...\n");
2098 	list_FreeAll(&button_l);
2099 	list_FreeAll(&entitiesdeleted);
2100 	if ( fancyWindow_bmp )
2101 	{
2102 		SDL_FreeSurface(fancyWindow_bmp);
2103 	}
2104 	if ( font8x8_bmp )
2105 	{
2106 		SDL_FreeSurface(font8x8_bmp);
2107 	}
2108 	if ( font12x12_bmp )
2109 	{
2110 		SDL_FreeSurface(font12x12_bmp);
2111 	}
2112 	if ( font16x16_bmp )
2113 	{
2114 		SDL_FreeSurface(font16x16_bmp);
2115 	}
2116 	if ( ttf8 )
2117 	{
2118 		TTF_CloseFont(ttf8);
2119 	}
2120 	if ( ttf12 )
2121 	{
2122 		TTF_CloseFont(ttf12);
2123 	}
2124 	if ( ttf16 )
2125 	{
2126 		TTF_CloseFont(ttf16);
2127 	}
2128 
2129 	printlog("freeing map data...\n");
2130 	if ( map.entities != NULL )
2131 	{
2132 		list_FreeAll(map.entities);
2133 		free(map.entities);
2134 	}
2135 	if ( map.creatures != nullptr)
2136 	{
2137 		list_FreeAll(map.creatures); //TODO: Need to call this? Entities are only pointed to by the thing, not owned.
2138 		delete map.creatures;
2139 	}
2140 	list_FreeAll(&light_l);
2141 	if ( map.tiles != NULL )
2142 	{
2143 		free(map.tiles);
2144 	}
2145 	if ( lightmap != NULL )
2146 	{
2147 		free(lightmap);
2148 	}
2149 	if ( lightmapSmoothed )
2150 	{
2151 		free(lightmapSmoothed);
2152 	}
2153 	if ( vismap != NULL )
2154 	{
2155 		free(vismap);
2156 	}
2157 
2158 	for ( c = 0; c < HASH_SIZE; c++ )
2159 	{
2160 		list_FreeAll(&ttfTextHash[c]);
2161 	}
2162 
2163 	// free textures
2164 	printlog("freeing textures...\n");
2165 	if ( tiles != NULL )
2166 	{
2167 		for ( c = 0; c < numtiles; c++ )
2168 		{
2169 			if ( tiles[c] )
2170 			{
2171 				SDL_FreeSurface(tiles[c]);
2172 			}
2173 		}
2174 		free(tiles);
2175 	}
2176 	if ( animatedtiles )
2177 	{
2178 		free(animatedtiles);
2179 		animatedtiles = nullptr;
2180 	}
2181 	if ( lavatiles )
2182 	{
2183 		free(lavatiles);
2184 		lavatiles = nullptr;
2185 	}
2186 	if ( swimmingtiles )
2187 	{
2188 		free(swimmingtiles);
2189 		swimmingtiles = nullptr;
2190 	}
2191 
2192 	// free sprites
2193 	printlog("freeing sprites...\n");
2194 	if ( sprites != NULL )
2195 	{
2196 		for ( c = 0; c < numsprites; c++ )
2197 		{
2198 			if ( sprites[c] )
2199 			{
2200 				SDL_FreeSurface(sprites[c]);
2201 			}
2202 		}
2203 		free(sprites);
2204 	}
2205 
2206 	// free achievement images
2207 	for (auto& item : achievementImages)
2208 	{
2209 		SDL_FreeSurface(item.second);
2210 	}
2211 	achievementImages.clear();
2212 
2213 	// free models
2214 	printlog("freeing models...\n");
2215 	if ( models != NULL )
2216 	{
2217 		for ( c = 0; c < nummodels; c++ )
2218 		{
2219 			if ( models[c] != NULL )
2220 			{
2221 				if ( models[c]->data )
2222 				{
2223 					free(models[c]->data);
2224 				}
2225 				free(models[c]);
2226 			}
2227 		}
2228 		free(models);
2229 	}
2230 	if ( polymodels != NULL )
2231 	{
2232 		for ( c = 0; c < nummodels; c++ )
2233 		{
2234 			if ( polymodels[c].faces )
2235 			{
2236 				free(polymodels[c].faces);
2237 			}
2238 		}
2239 		if ( !disablevbos )
2240 		{
2241 			for ( c = 0; c < nummodels; c++ )
2242 			{
2243 				if ( polymodels[c].vbo )
2244 				{
2245 					SDL_glDeleteBuffers(1, &polymodels[c].vbo);
2246 				}
2247 				if ( polymodels[c].colors )
2248 				{
2249 					SDL_glDeleteBuffers(1, &polymodels[c].colors);
2250 				}
2251 				if ( polymodels[c].va )
2252 				{
2253 					SDL_glDeleteVertexArrays(1, &polymodels[c].va);
2254 				}
2255 			}
2256 		}
2257 		free(polymodels);
2258 	}
2259 
2260 	// free sounds
2261 #ifdef USE_FMOD
2262 	printlog("freeing sounds...\n");
2263 	if ( sounds != NULL )
2264 	{
2265 		for ( c = 0; c < numsounds && !no_sound; c++ )
2266 		{
2267 			if (sounds[c] != NULL)
2268 			{
2269 				if (sounds[c] != NULL)
2270 				{
2271 					FMOD_Sound_Release(sounds[c]);    //Free the sound's FMOD sound.
2272 				}
2273 				//free(sounds[c]); //Then free the sound itself.
2274 			}
2275 		}
2276 		free(sounds); //Then free the sound array.
2277 	}
2278 #endif
2279 
2280 	// delete opengl buffers
2281 	if ( allsurfaces != NULL )
2282 	{
2283 		free(allsurfaces);
2284 	}
2285 	if ( texid != NULL )
2286 	{
2287 		glDeleteTextures(MAXTEXTURES, texid);
2288 		free(texid);
2289 	}
2290 
2291 	// delete opengl buffers
2292 	/*SDL_glDeleteBuffers(MAXBUFFERS,vboid);
2293 	if( vboid != NULL )
2294 		free(vboid);
2295 	SDL_glDeleteVertexArrays(MAXBUFFERS,vaoid);
2296 	if( vaoid != NULL )
2297 		free(vaoid);*/
2298 
2299 	// close network interfaces
2300 	closeNetworkInterfaces();
2301 
2302 	// shutdown SDL subsystems
2303 	printlog("shutting down SDL and its subsystems...\n");
2304 	SDLNet_Quit();
2305 	IMG_Quit();
2306 	//Mix_HaltChannel(-1);
2307 	//Mix_CloseAudio();
2308 #ifdef USE_FMOD
2309 	if ( fmod_system )
2310 	{
2311 		FMOD_System_Close(fmod_system);
2312 		FMOD_System_Release(fmod_system);
2313 		fmod_system = NULL;
2314 	}
2315 #endif
2316 	if ( screen )
2317 	{
2318 		SDL_DestroyWindow(screen);
2319 		screen = NULL;
2320 	}
2321 	if ( renderer )
2322 	{
2323 #ifdef APPLE
2324 		SDL_DestroyRenderer(renderer);
2325 #else
2326 		SDL_GL_DeleteContext(renderer);
2327 #endif
2328 		renderer = NULL;
2329 	}
2330 	if ( mainsurface )
2331 	{
2332 		SDL_FreeSurface(mainsurface);
2333 		mainsurface = NULL;
2334 	}
2335 	TTF_Quit();
2336 	SDL_Quit();
2337 
2338 	// free video and input buffers
2339 	if ( zbuffer != NULL )
2340 	{
2341 		free(zbuffer);
2342 	}
2343 	if ( clickmap != NULL )
2344 	{
2345 		free(clickmap);
2346 	}
2347 
2348 	// shutdown steamworks
2349 #ifdef STEAMWORKS
2350 	if ( steam_init )
2351 	{
2352 		printlog("storing user stats to Steam...\n");
2353 		SteamUserStats()->StoreStats();
2354 		if ( g_SteamLeaderboards )
2355 		{
2356 			delete g_SteamLeaderboards;
2357 		}
2358 		if ( g_SteamWorkshop )
2359 		{
2360 			delete g_SteamWorkshop;
2361 		}
2362 		if ( g_SteamStatistics )
2363 		{
2364 			delete g_SteamStatistics;
2365 		}
2366 		SteamAPI_Shutdown();
2367 	}
2368 #endif
2369 
2370 
2371 	int numLogFilesToKeepInArchive = 30;
2372 	// archive logfiles.
2373 	char lognamewithTimestamp[128];
2374 	std::time_t timeNow = std::time(nullptr);
2375 	struct tm *localTimeNow = nullptr;
2376 	localTimeNow = std::localtime(&timeNow);
2377 
2378 	snprintf(lognamewithTimestamp, 127, "log_%4d%02d%02d_%02d%02d%02d.txt",
2379 		localTimeNow->tm_year + 1900, localTimeNow->tm_mon + 1, localTimeNow->tm_mday, localTimeNow->tm_hour, localTimeNow->tm_min, localTimeNow->tm_sec);
2380 
2381 	std::string logarchivePath = outputdir;
2382 	logarchivePath.append(PHYSFS_getDirSeparator()).append("logfiles").append(PHYSFS_getDirSeparator());
2383 	std::string logarchiveFilePath = logarchivePath + lognamewithTimestamp;
2384 
2385 
2386 	// prune any old logfiles if qty >= numLogFilesToKeepInArchive
2387 	std::vector<std::pair<int, std::string>> sortedLogFiles;
2388 	auto archivedFiles = directoryContents(logarchivePath.c_str(), false, true);
2389 	if ( !archivedFiles.empty() && archivedFiles.size() >= numLogFilesToKeepInArchive )
2390 	{
2391 		// first find the date modified of log files.
2392 		for ( auto file : archivedFiles )
2393 		{
2394 			struct tm *tm = nullptr;
2395 //#ifdef WINDOWS
2396 			std::string filePath = logarchivePath + file;
2397 #ifdef WINDOWS
2398 			struct _stat fileDateModified;
2399 			if ( _stat(filePath.c_str(), &fileDateModified) == 0 )
2400 #else
2401 			struct stat fileDateModified;
2402 			if ( stat(filePath.c_str(), &fileDateModified) == 0 )
2403 #endif
2404 			{
2405 				tm = localtime(&fileDateModified.st_mtime);
2406 			}
2407 //#else
2408 			// UNIX/MAC
2409 			/*struct stat fileDateModified;
2410 			if ( stat(filePath.c_str(), &fileDateModified) == 0 )
2411 			{
2412 			tm = localtime(&fileDateModified.st_mtime);
2413 			}*/
2414 //#endif
2415 			if ( tm )
2416 			{
2417 				int timeDifference = std::difftime(timeNow, mktime(tm));
2418 				sortedLogFiles.push_back(std::make_pair(timeDifference, file));
2419 			}
2420 		}
2421 	}
2422 	std::sort(sortedLogFiles.begin(), sortedLogFiles.end()); // sort most recent to oldest.
2423 	while ( sortedLogFiles.size() >= numLogFilesToKeepInArchive )
2424 	{
2425 		std::string logToRemove = logarchivePath + sortedLogFiles.back().second;
2426 		printlog("notice: Deleting archived log file %s due to number of old log files (%d) exceeds limit of %d.", logToRemove.c_str(), sortedLogFiles.size(), numLogFilesToKeepInArchive);
2427 		if ( access(logToRemove.c_str(), F_OK) != -1 )
2428 		{
2429 			int result = remove(logToRemove.c_str());
2430 			if ( result )
2431 			{
2432 				printlog("warning: failed to delete logfile %s", logToRemove.c_str());
2433 			}
2434 		}
2435 		else
2436 		{
2437 			printlog("warning: could not access logfile %s", logToRemove.c_str());
2438 		}
2439 		sortedLogFiles.pop_back();
2440 	}
2441 
2442 	if ( PHYSFS_isInit() )
2443 	{
2444 		PHYSFS_deinit();
2445 		printlog("[PhysFS]: De-initializing...\n");
2446 	}
2447 
2448 	// free currently loaded language if any
2449 	freeLanguages();
2450 	printlog("notice: archiving log file as %s...\n", logarchiveFilePath.c_str());
2451 	printlog("success\n");
2452 	fclose(logfile);
2453 
2454 	// copy the log file into the archives.
2455 	char logToArchive[PATH_MAX];
2456 	completePath(logToArchive, "log.txt", outputdir);
2457 #ifdef WINDOWS
2458 	CopyFileA(logToArchive, logarchiveFilePath.c_str(), false);
2459 #else //LINUX & APPLE
2460 	std::stringstream ss;
2461 	ss << "cp " << logToArchive << " " << logarchiveFilePath.c_str();
2462 	system(ss.str().c_str());
2463 #endif // WINDOWS
2464 	return 0;
2465 }
2466 
2467 #ifdef PANDORA
GO_InitFBO()2468 void GO_InitFBO()
2469 {
2470 	glBindFramebuffer(GL_FRAMEBUFFER, 0);
2471 	if(fbo_fbo) {
2472 		glDeleteFramebuffers(1, &fbo_fbo); fbo_fbo = 0;
2473 		glDeleteRenderbuffers(1, &fbo_ren); fbo_ren = 0;
2474 		if(fbo_trn) {
2475 			glDeleteRenderbuffers(1, &fbo_trn); fbo_trn = 0;
2476 		}
2477 		if(fbo_tex) {
2478 			glDeleteTextures(1, &fbo_tex); fbo_tex = 0;
2479 		}
2480 	}
2481 
2482 	// Pandora, create the FBO!
2483 	bool small_fbo=((xres==800) && (yres==480));
2484 	glGenFramebuffers(1, &fbo_fbo);
2485 	glBindFramebuffer(GL_FRAMEBUFFER, fbo_fbo);
2486 	if(small_fbo) {
2487 		glGenRenderbuffers(1, &fbo_trn);
2488 		glBindRenderbuffer(GL_RENDERBUFFER, fbo_trn);
2489 		glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1024, 512);
2490 		glBindRenderbuffer(GL_RENDERBUFFER, 0);
2491 		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fbo_trn);
2492 	} else {
2493 		glGenTextures(1, &fbo_tex);
2494 		glBindTexture(GL_TEXTURE_2D, fbo_tex);
2495 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2496 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2497 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2498 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2499 		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
2500 		glBindTexture(GL_TEXTURE_2D, 0);
2501 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex, 0);
2502 	}
2503 	glGenRenderbuffers(1, &fbo_ren);
2504 	glBindRenderbuffer(GL_RENDERBUFFER, fbo_ren);
2505 	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 1024, (small_fbo)?512:1024);
2506 	glBindRenderbuffer(GL_RENDERBUFFER, 0);
2507 	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_ren);
2508 	glBindFramebuffer(GL_FRAMEBUFFER, 0);
2509 	if(!small_fbo)
2510 		glBindFramebuffer(GL_FRAMEBUFFER, fbo_fbo);
2511 
2512 }
2513 #endif
2514 
2515 /*-------------------------------------------------------------------------------
2516 
2517 	initVideo
2518 
2519 	Sets the SDL/openGL context using global video variables
2520 
2521 -------------------------------------------------------------------------------*/
2522 
initVideo()2523 bool initVideo()
2524 {
2525 	SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1/*3*/ ); //Why GL 3.0? using only fixed pipeline stuff here
2526 	SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
2527 	SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
2528 	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
2529 	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
2530 	//SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
2531 	//SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
2532 	//SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4 );
2533 
2534 	printlog("setting display mode to %dx%d...\n", xres, yres);
2535 	Uint32 flags = 0;
2536 #ifdef PANDORA
2537 	fullscreen = true;
2538 #endif
2539 	if ( fullscreen )
2540 	{
2541 		flags |= SDL_WINDOW_FULLSCREEN;
2542 	}
2543 	if ( borderless )
2544 	{
2545 		flags |= SDL_WINDOW_BORDERLESS;
2546 	}
2547 	if ( !game )
2548 	{
2549 		flags |= SDL_WINDOW_RESIZABLE;
2550 	}
2551 	if ( !softwaremode )
2552 	{
2553 		flags |= SDL_WINDOW_OPENGL;
2554 	}
2555 #ifdef APPLE
2556 	if ( fullscreen )
2557 	{
2558 		flags |= SDL_WINDOW_BORDERLESS;
2559 	}
2560 	SDL_DestroyWindow(screen);
2561 	screen = NULL;
2562 #endif
2563 #ifdef PANDORA
2564 	int screen_width = 800;
2565 #else
2566 	int screen_width = xres;
2567 #endif
2568 	if ( !screen )
2569 	{
2570 #ifdef PANDORA
2571 		if ((screen = SDL_CreateWindow( window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, 480, flags )) == NULL)
2572 #else
2573 		if ((screen = SDL_CreateWindow( window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, yres, flags )) == NULL)
2574 #endif
2575 		{
2576 			printlog("failed to set video mode.\n");
2577 			return false;
2578 		}
2579 	}
2580 	else
2581 	{
2582 #ifdef PANDORA
2583 		SDL_SetWindowSize(screen, screen_width, 480);
2584 #else
2585 		SDL_SetWindowSize(screen, screen_width, yres);
2586 #endif
2587 		if ( fullscreen )
2588 		{
2589 			SDL_SetWindowFullscreen(screen, SDL_WINDOW_FULLSCREEN);
2590 		}
2591 		else
2592 		{
2593 			SDL_SetWindowFullscreen(screen, 0);
2594 		}
2595 		if ( borderless )
2596 		{
2597 			SDL_SetWindowBordered(screen, SDL_bool::SDL_FALSE);
2598 		}
2599 		else
2600 		{
2601 			SDL_SetWindowBordered(screen, SDL_bool::SDL_TRUE);
2602 		}
2603 		SDL_SetWindowPosition(screen, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
2604 	}
2605 	if ( !renderer )
2606 	{
2607 #ifdef APPLE
2608 		if ((renderer = SDL_CreateRenderer(screen, -1, 0)) == NULL)
2609 		{
2610 #else
2611 		if ((renderer = SDL_GL_CreateContext(screen)) == NULL)
2612 		{
2613 #endif
2614 			printlog("failed to create SDL renderer. Reason: \"%s\"\n", SDL_GetError());
2615 			printlog("You may need to update your video drivers.\n");
2616 			return false;
2617 		}
2618 	}
2619 #ifndef APPLE
2620 	SDL_GL_MakeCurrent(screen, renderer);
2621 #endif
2622 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
2623 	Uint32 rmask = 0xff000000;
2624 	Uint32 gmask = 0x00ff0000;
2625 	Uint32 bmask = 0x0000ff00;
2626 	Uint32 amask = 0x000000ff;
2627 #else
2628 	Uint32 rmask = 0x000000ff;
2629 	Uint32 gmask = 0x0000ff00;
2630 	Uint32 bmask = 0x00ff0000;
2631 	Uint32 amask = 0xff000000;
2632 #endif
2633 	if ((mainsurface = SDL_CreateRGBSurface(0, xres, yres, 32, rmask, gmask, bmask, amask)) == NULL)
2634 	{
2635 		printlog("failed to create main window surface.\n");
2636 		return false;
2637 	}
2638 	if ( !softwaremode )
2639 	{
2640 #ifdef PANDORA
2641 		GO_InitFBO();
2642 #endif
2643 		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2644 		glEnable(GL_TEXTURE_2D);
2645 		glEnable(GL_CULL_FACE);
2646 		glCullFace(GL_BACK);
2647 		glEnable(GL_BLEND);
2648 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2649 		//glEnableClientState(GL_VERTEX_ARRAY);
2650 		glMatrixMode( GL_MODELVIEW );
2651 		glLoadIdentity();
2652 		glMatrixMode( GL_PROJECTION );
2653 		glLoadIdentity();
2654 		glClearColor( 0, 0, 0, 0 );
2655 	}
2656 
2657 	if ( verticalSync )
2658 	{
2659 		SDL_GL_SetSwapInterval(1);
2660 	}
2661 	else
2662 	{
2663 		SDL_GL_SetSwapInterval(0);
2664 	}
2665 	if ( SDL_SetWindowBrightness(screen, vidgamma) < 0 )
2666 	{
2667 		printlog("warning: failed to change gamma setting:\n%s\n", SDL_GetError());
2668 		return true;
2669 	}
2670 	printlog("display changed successfully.\n");
2671 	return true;
2672 }
2673 
2674 /*-------------------------------------------------------------------------------
2675 
2676 	changeVideoMode
2677 
2678 	In windows: saves the openGL context, sets the video mode, and restores
2679 	the context
2680 	otherwise: acts as a wrapper for initVideo
2681 
2682 -------------------------------------------------------------------------------*/
2683 
2684 bool changeVideoMode()
2685 {
2686 	printlog("changing video mode.\n");
2687 #ifdef PANDORA
2688 	GO_InitFBO();
2689 #else
2690 	int c;
2691 
2692 	// delete old texture names (they're going away anyway)
2693 	glDeleteTextures(MAXTEXTURES, texid);
2694 
2695 	// delete vertex data
2696 	if ( !disablevbos )
2697 	{
2698 		for ( c = 0; c < nummodels; c++ )
2699 		{
2700 			SDL_glDeleteBuffers(1, &polymodels[c].vbo);
2701 			SDL_glDeleteBuffers(1, &polymodels[c].colors);
2702 			SDL_glDeleteVertexArrays(1, &polymodels[c].va);
2703 		}
2704 	}
2705 
2706 	/*if( screen ) {
2707 		SDL_DestroyWindow(screen);
2708 		screen = NULL;
2709 	}*/
2710 	if ( renderer )
2711 	{
2712 #ifdef APPLE
2713 		SDL_DestroyRenderer(renderer);
2714 #else
2715 		SDL_GL_DeleteContext(renderer);
2716 #endif
2717 		renderer = NULL;
2718 	}
2719 	if ( mainsurface )
2720 	{
2721 		SDL_FreeSurface(mainsurface);
2722 		mainsurface = NULL;
2723 	}
2724 
2725 	// set video mode
2726 	int result = initVideo();
2727 	if ( !result )
2728 	{
2729 		xres = 960;
2730 		yres = 600;
2731 		fullscreen = 0;
2732 		borderless = false;
2733 		printlog("defaulting to safe video mode...\n");
2734 		if ( !initVideo() )
2735 		{
2736 			return false;
2737 		}
2738 	}
2739 
2740 	// now reload all textures
2741 	glGenTextures(MAXTEXTURES, texid);
2742 	for ( c = 1; c < imgref; c++ )
2743 	{
2744 		glLoadTexture(allsurfaces[c], c);
2745 	}
2746 
2747 	// regenerate vbos
2748 	if ( !disablevbos )
2749 	{
2750 		generateVBOs(0, nummodels);
2751 	}
2752 #endif
2753 	// success
2754 	return true;
2755 }
2756 
2757 /*-------------------------------------------------------------------------------
2758 
2759 loadItemLists()
2760 
2761 loads the global item whitelist/blacklists and level curve.
2762 
2763 -------------------------------------------------------------------------------*/
2764 
2765 bool loadItemLists()
2766 {
2767 	//FILE* fp;
2768 	int c;
2769 
2770 	// open log file
2771 	if ( !logfile )
2772 	{
2773 		openLogFile();
2774 	}
2775 
2776 	// compose filename
2777 	//char filename[128] = "items/items_global.txt";
2778 	std::string itemsTxtDirectory = PHYSFS_getRealDir("items/items_global.txt");
2779 	itemsTxtDirectory.append(PHYSFS_getDirSeparator()).append("items/items_global.txt");
2780 	// check if item list is valid
2781 	if ( !dataPathExists(itemsTxtDirectory.c_str()) )
2782 	{
2783 		// file doesn't exist
2784 		printlog("error: unable to locate global item list file: '%s'", itemsTxtDirectory.c_str());
2785 		return false;
2786 	}
2787 
2788 	std::vector<std::string> itemLevels = getLinesFromDataFile(itemsTxtDirectory);
2789 	std::string line;
2790 	int itemIndex = 0;
2791 
2792 	for ( std::vector<std::string>::const_iterator i = itemLevels.begin(); i != itemLevels.end(); ++i ) {
2793 		// process i
2794 		line = *i;
2795 		if ( line[0] == '#' || line[0] == '\n' )
2796 		{
2797 			continue;
2798 		}
2799 		std::size_t found = line.find('#');
2800 		if ( found != std::string::npos )
2801 		{
2802 			char tmp[128];
2803 			std::string sub = line.substr(0, found);
2804 			strncpy(tmp, sub.c_str(), sub.length());
2805 			tmp[sub.length()] = '\0';
2806 			//printlog("%s", tmp);
2807 			items[itemIndex].level = atoi(tmp);
2808 			++itemIndex;
2809 		}
2810 	}
2811 
2812 	printlog("successfully loaded global item list '%s' \n", itemsTxtDirectory.c_str());
2813 	/*for ( c = 0; c < NUMITEMS; ++c )
2814 	{
2815 		printlog("%s level: %d", items[c].name_identified, items[c].level);
2816 	}*/
2817 	return true;
2818 }
2819