1 //---------------------------------------------------------------------------
2 #include "stdafx.h"
3 
4 #include <cstdio> // n.b., needed on Linux at least
5 #include <cstring> // n.b., needed on Linux at least
6 #include <cstdlib> // n.b., needed on Linux at least
7 #include <cstdarg> // n.b., needed on Linux at least
8 
9 #ifdef _WIN32
10 #include <shlobj.h>
11 #include <Shlwapi.h>
12 #pragma comment(lib,"shlwapi.lib")
13 
14 #include <io.h> // for access
15 #include <direct.h> // for mkdir
16 #define access _access
17 #define mkdir _mkdir
18 #elif __DragonFly__
19 #include <sys/stat.h> // for mkdir
20 #include <unistd.h> // for access
21 #endif
22 
23 #if defined(__ANDROID__)
24 #include <android/log.h>
25 #endif
26 
27 #include <cassert>
28 #include <cmath> // n.b., needed on Linux at least
29 
30 #include "utils.h"
31 #include "common.h"
32 
33 //---------------------------------------------------------------------------
34 
35 //const bool DEBUG = true;
36 //const int DEBUGLEVEL = 4;
37 
38 /* Return probability (as a proportion of RAND_MAX) that at least one poisson event
39 * occurred within the time_interval, given the mean number of time units per event.
40 */
poisson(int mean_ticks_per_event,int time_interval)41 int poisson(int mean_ticks_per_event,int time_interval) {
42 	if( mean_ticks_per_event == 0 )
43 		return RAND_MAX;
44 	ASSERT( mean_ticks_per_event > 0 );
45 	int prob = (int)(RAND_MAX * ( 1.0 - exp( - ((double)time_interval) / mean_ticks_per_event ) ));
46 	return prob;
47 }
48 
n_digits(int number)49 int n_digits(int number) {
50 	int num = 0;
51 	if( number < 0 ) {
52 		number = - number;
53 		num++;
54 	}
55 	while( number != 0 ) {
56 		num++;
57 		number /= 10;
58 	}
59 	return num;
60 }
61 
textLines(int * n_lines,int * max_wid,const char * text,int lower_w,int upper_w)62 void textLines(int *n_lines,int *max_wid,const char *text, int lower_w, int upper_w) {
63 	*n_lines = 0;
64 	*max_wid = 0;
65 	const char *ptr = text;
66 	const char *n_ptr = ptr;
67 	for(;;) {
68 		*n_lines = *n_lines + 1;
69 
70 		int wid = 0;
71 		while( *n_ptr != '\n' && *n_ptr != '\0' ) {
72 			if( isupper(*n_ptr) )
73 				wid += upper_w;
74 			else
75 				wid += lower_w;
76 			n_ptr++;
77 		}
78 
79 		//int wid = (int)(n_ptr - ptr);
80 		if( wid > *max_wid )
81 			*max_wid = wid;
82 
83 		if( *n_ptr == '\0' )
84 			break;
85 		n_ptr++;
86 		ptr = n_ptr;
87 	}
88 }
89 
90 char application_name[] = "Gigalomania";
91 
92 const char *logfilename = NULL;
93 const char *oldlogfilename = NULL;
94 
95 // Maemo/Meego treated as Linux as far as paths are concerned
96 #if _WIN32
97 char application_path[MAX_PATH] = "";
98 #elif defined(__ANDROID__)
99 char application_path[] = "/sdcard/net.sourceforge.gigalomania";
100 const char *application_path_uninstall = NULL; // path for folder that will be deleted upon uninstall
101 #elif __DragonFly__
102 char *application_path = NULL;
103 #else
104 char application_path[] = "";
105 #endif
106 
107 /* Determines location of the folders for storing userdata, creating the sub-folders if necessary.
108  * For Windows, this is in %APPDATA%/application_name/
109  * For Linux (including Maemo and Meego), this is in user's home/.config/application_name/ (note the '.', to make it a hidden folder)
110  * If the folder can't be accessed (or running on a new operating system), the program folder is used.
111  * Must be called before getApplicationFilename() or initLogFile().
112  */
initFolderPaths()113 void initFolderPaths() {
114     LOG("initFolderPaths()\n"); // n.b., at this stage logging will only go to console output, not to log file
115 
116 #if _WIN32
117 	bool ok = true;
118 	WCHAR application_path_w[MAX_PATH];
119     if ( SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_APPDATA,
120                                      NULL, 0, application_path_w ) ) ) {
121 		{
122 			// handle unicode (e.g., for unicode user accounts)
123 			int shortpath_length_w = GetShortPathNameW(application_path_w,0,0);
124 			LPWSTR shortpath_w = new WCHAR[shortpath_length_w];
125 			GetShortPathNameW(application_path_w,shortpath_w,shortpath_length_w);
126 			int shortpath_length = WideCharToMultiByte(CP_OEMCP, WC_NO_BEST_FIT_CHARS, shortpath_w, shortpath_length_w, 0, 0, 0, 0);
127 			WideCharToMultiByte(CP_OEMCP, WC_NO_BEST_FIT_CHARS, shortpath_w, shortpath_length_w, application_path, MAX_PATH, 0, 0);
128 			delete [] shortpath_w;
129 		}
130         PathAppendA(application_path, application_name);
131 
132 		if( access(application_path, 0) != 0 ) {
133 			// folder doesn't seem to exist - try creating it
134 			int res = mkdir(application_path);
135 			//int res = 1; // test
136 			if( res != 0 ) {
137 				printf("Failed to create folder for application data!\n");
138 				MessageBoxA(NULL, "Failed to create folder for application data - storing in local folder instead.\n", "Warning", MB_OK|MB_ICONEXCLAMATION);
139 				ok = false;
140 			}
141 		}
142     }
143 	else {
144 		printf("Failed to obtain path for application folder!\n");
145 		MessageBoxA(NULL, "Failed to obtain path for application folder - storing in local folder instead.\n", "Warning", MB_OK|MB_ICONEXCLAMATION);
146 		ok = false;
147 	}
148 
149 	if( !ok ) {
150 		// just save in local directory and hope for the best!
151 		strcpy(application_path, "");
152 	}
153 #elif defined(__ANDROID__)
154 	// application_path already defined above
155 	// create the folder if it doesn't already exist
156 	bool ok = true;
157 	if( access(application_path, 0) != 0 ) {
158 		__android_log_print(ANDROID_LOG_INFO, "Gigalomania", "try to create data folder");
159 		int res = mkdir(application_path, S_IRWXU | S_IRWXG | S_IRWXO);
160 		if( res != 0 ) {
161 			__android_log_print(ANDROID_LOG_INFO, "Gigalomania", "failed to create data folder");
162 			ok = false;
163 		}
164 	}
165 
166 	if( !ok ) {
167 		// just save in local directory and hope for the best!
168 		strcpy(application_path, "");
169 	}
170 
171 	// find the folder that will be deleted upon uninstall - shouldn't need to create this folder
172 	application_path_uninstall = SDL_AndroidGetExternalStoragePath();
173 	if( application_path_uninstall == NULL ) {
174 		// just save in local directory and hope for the best!
175 		__android_log_print(ANDROID_LOG_INFO, "Gigalomania", "SDL_AndroidGetExternalStoragePath returned NULL");
176 		char *path = new char[1];
177 		strcpy(path, "");
178 		application_path_uninstall = path;
179 	}
180 #elif __DragonFly__
181 	char *homedir = getenv("HOME");
182 	const char *subdir = "/.config/gigalomania";
183 	int len = strlen(homedir) + strlen(subdir);
184 	application_path = new char[len+1];
185 	sprintf(application_path, "%s%s", homedir, subdir);
186 
187 	// create the folder if it doesn't already exist
188 	bool ok = true;
189 	if( access(application_path, 0) != 0 ) {
190 		int res = mkdir(application_path, S_IRWXU | S_IRWXG | S_IRWXO);
191 		if( res != 0 ) {
192 			ok = false;
193 		}
194 	}
195 
196 	if( !ok ) {
197 		// just save in local directory and hope for the best!
198 		strcpy(application_path, "");
199 	}
200 #else
201 	// no need to do anything
202 #endif
203 }
204 
205 /* Returns a full path for a filename in userspace (i.e., where we'll have read/write access). See initFolderPaths() for details.
206  * Must be called after initFolderPaths().
207  * If survive_uninstall is true, a path will be returned that survives uninstallation. This only makes a difference on Android.
208  */
getApplicationFilename(const char * name,bool survive_uninstall)209 const char *getApplicationFilename(const char *name, bool survive_uninstall) {
210     // not safe to use LOG here, as logfile may not have been initialised!
211     //printf("getApplicationFilename: %s\n", name);
212     //printf("application_path: %s\n", application_path);
213     // Maemo/Meego treated as Linux as far as paths are concerned
214 #if _WIN32
215 	char *filename = new char[MAX_PATH];
216 	strcpy(filename, application_path);
217 	PathAppendA(filename, name);
218 #elif __DragonFly__ // also covers Maemo, Meego and Android
219 	const char *path = application_path;
220 #if defined(__ANDROID__)
221 	// application_path already points to a folder that survives uninistallation, so only need to handle application_path being false
222 	if( !survive_uninstall ) {
223 		path = application_path_uninstall;
224 	}
225 #endif
226 
227 	char *filename = NULL;
228 	int path_len = strlen(path);
229 	if( path_len == 0 || path[path_len-1] == '/' ) {
230 		// shouldn't add path separator
231 		int len = path_len + strlen(name);
232 		filename = new char[len+1];
233 		sprintf(filename, "%s%s", path, name);
234 	}
235 	else {
236 		// should add path separator
237 		int len = path_len + 1 + strlen(name);
238 		filename = new char[len+1];
239 		sprintf(filename, "%s/%s", path, name);
240 	}
241 #else
242 	char *filename = new char[strlen(name)+1];
243 	strcpy(filename, name);
244 #endif
245 	//printf("getApplicationFilename returns: %s\n", filename);
246     return filename;
247 }
248 
249 /* Initialises the log files.
250  * Must be called after initFolderPaths().
251  */
initLogFile()252 void initLogFile() {
253     LOG("initLogFile()\n"); // n.b., at this stage logging will only go to console output, not to log file
254 	logfilename = getApplicationFilename("log.txt", false);
255 	oldlogfilename = getApplicationFilename("log_old.txt", false);
256 
257 	remove(oldlogfilename);
258 	rename(logfilename, oldlogfilename);
259 	remove(logfilename);
260 
261 	LOG("Initialising Log File...\n");
262 	LOG("Version %d.%d\n", majorVersion, minorVersion);
263 
264 #ifdef _DEBUG
265 	LOG("Running in Debug mode\n");
266 #else
267 	LOG("Running in Release mode\n");
268 #endif
269 
270 #if defined(_WIN32)
271     LOG("Platform: Windows\n");
272 #elif defined(__ANDROID__)
273 	// must be before __DragonFly__, as Android also defines __linux
274 	LOG("Platform: Android\n");
275 #elif __DragonFly__
276 	LOG("Platform: Linux\n");
277 #elif defined(__APPLE__) && defined(__MACH__)
278 	LOG("Platform: MacOS X\n");
279 #elif __amigaos4__
280 	// must be before AROS, as the AmigaOS 4 makefile defines AROS too
281     LOG("Platform: AmigaOS 4\n");
282 #elif AROS
283     LOG("Platform: AROS\n");
284 #elif defined(__MORPHOS__)
285     LOG("Platform: MorphOS\n");
286 #else
287 	LOG("Platform: UNKNOWN\n");
288 #endif
289 
290 	LOG("Application path: %s\n", application_path);
291 	LOG("logfilename: %s\n", logfilename);
292 	LOG("oldlogfilename: %s\n", oldlogfilename);
293 }
294 
cleanupLogFile()295 void cleanupLogFile() {
296     LOG("cleanupLogFile()\n");
297 	if( logfilename != NULL ) {
298 		delete [] logfilename;
299 	}
300 	if( oldlogfilename != NULL ) {
301 		delete [] oldlogfilename;
302 	}
303 }
304 
log(const char * text,...)305 bool log(const char *text,...) {
306 	//return true;
307 	// n.b., on Ubuntu Linux at least, need to have a separate va_list every time we use it
308 #if defined(__ANDROID__)
309 	{
310 		va_list vlist;
311 		va_start(vlist, text);
312 		__android_log_vprint(ANDROID_LOG_INFO, "Gigalomania", text, vlist);
313 		va_end(vlist);
314 	}
315 #endif
316 	if( logfilename != NULL ) {
317 		FILE *logfile = fopen(logfilename,"at+");
318 		if( logfile != NULL ) {
319 			va_list vlist;
320 			va_start(vlist, text);
321 			vfprintf(logfile, text, vlist);
322 			va_end(vlist);
323 			fclose(logfile);
324 			logfile = NULL;
325 		}
326 	}
327 	if( debugwindow ) {
328 		va_list vlist;
329 		va_start(vlist, text);
330 		vprintf(text, vlist);
331 		va_end(vlist);
332 	}
333 
334 	return true;
335 }
336 
337 // Perlin noise
338 
339 #define B 0x100
340 #define BM 0xff
341 
342 #define N 0x1000
343 #define NP 12   /* 2^N */
344 #define NM 0xfff
345 
346 static int p[B + B + 2];
347 static float g3[B + B + 2][3];
348 static float g2[B + B + 2][2];
349 static float g1[B + B + 2];
350 static int start = 1;
351 
normalize2(float v[2])352 static void normalize2(float v[2])
353 {
354 	float s;
355 
356 	s = sqrt(v[0] * v[0] + v[1] * v[1]);
357 	v[0] = v[0] / s;
358 	v[1] = v[1] / s;
359 }
360 
normalize3(float v[3])361 static void normalize3(float v[3])
362 {
363 	float s;
364 
365 	s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
366 	v[0] = v[0] / s;
367 	v[1] = v[1] / s;
368 	v[2] = v[2] / s;
369 }
370 
initPerlin()371 void initPerlin() {
372 	start = 0;
373 	int i, j, k;
374 
375 	for (i = 0 ; i < B ; i++) {
376 		p[i] = i;
377 
378 		g1[i] = (float)((rand() % (B + B)) - B) / B;
379 
380 		for (j = 0 ; j < 2 ; j++)
381 			g2[i][j] = (float)((rand() % (B + B)) - B) / B;
382 		normalize2(g2[i]);
383 
384 		for (j = 0 ; j < 3 ; j++)
385 			g3[i][j] = (float)((rand() % (B + B)) - B) / B;
386 		normalize3(g3[i]);
387 	}
388 
389 	while (--i) {
390 		k = p[i];
391 		p[i] = p[j = rand() % B];
392 		p[j] = k;
393 	}
394 
395 	for (i = 0 ; i < B + 2 ; i++) {
396 		p[B + i] = p[i];
397 		g1[B + i] = g1[i];
398 		for (j = 0 ; j < 2 ; j++)
399 			g2[B + i][j] = g2[i][j];
400 		for (j = 0 ; j < 3 ; j++)
401 			g3[B + i][j] = g3[i][j];
402 	}
403 }
404 
405 #define s_curve(t) ( t * t * (3.0f - 2.0f * t) )
406 
407 #define lerp(t, a, b) ( a + t * (b - a) )
408 
409 #define setup(i,b0,b1,r0,r1)\
410 	t = vec[i] + N;\
411 	b0 = ((int)t) & BM;\
412 	b1 = (b0+1) & BM;\
413 	r0 = t - (int)t;\
414 	r1 = r0 - 1.0f;
415 
perlin_noise2(float vec[2])416 float perlin_noise2(float vec[2]) {
417 	int bx0, bx1, by0, by1, b00, b10, b01, b11;
418 	float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
419 	register int i, j;
420 
421 	if (start) {
422 		initPerlin();
423 	}
424 
425 	setup(0, bx0,bx1, rx0,rx1);
426 	setup(1, by0,by1, ry0,ry1);
427 
428 	i = p[ bx0 ];
429 	j = p[ bx1 ];
430 
431 	b00 = p[ i + by0 ];
432 	b10 = p[ j + by0 ];
433 	b01 = p[ i + by1 ];
434 	b11 = p[ j + by1 ];
435 
436 	sx = s_curve(rx0);
437 	sy = s_curve(ry0);
438 
439 #define at2(rx,ry) ( rx * q[0] + ry * q[1] )
440 
441 	q = g2[ b00 ] ; u = at2(rx0,ry0);
442 	q = g2[ b10 ] ; v = at2(rx1,ry0);
443 	a = lerp(sx, u, v);
444 
445 	q = g2[ b01 ] ; u = at2(rx0,ry1);
446 	q = g2[ b11 ] ; v = at2(rx1,ry1);
447 	b = lerp(sx, u, v);
448 
449 	return lerp(sy, a, b);
450 }
451 
452 #if defined(AROS) || defined(__MORPHOS__)
453 #ifdef __amigaos4__
454 #undef __USE_AMIGAOS_NAMESPACE__
455 #define __USE_INLINE__
456 #endif
457 
458 #include <proto/intuition.h>
459 
460 // This needs to be a separate function in a separate file, to avoid name collision with AROS/AmigaOS types Screen and Image.
461 // This also means we shouldn't do "using namespace Gigalomania" in utils.cpp, unless we move this function to its own file.
462 
getAROSScreenSize(int * user_width,int * user_height)463 void getAROSScreenSize(int *user_width, int *user_height) {
464 	// see http://wiki.amigaos.net/index.php/Intuition_Screens
465 	// setup a default in case we can't access the Workbench Screen for some reason
466 	*user_width = 640;
467 	*user_height = 480;
468 	struct Screen *my_wbscreen_ptr = LockPubScreen("Workbench");
469 	if( my_wbscreen_ptr == NULL ) {
470 		LOG("getAROSScreenSize: failed to lock Workbench screen\n");
471 	}
472 	else {
473 		*user_width = my_wbscreen_ptr->Width;
474 		*user_height = my_wbscreen_ptr->Height;
475 		LOG("getAROSScreenSize: Workbench screen size is %d x %d\n", *user_width, *user_height);
476 		UnlockPubScreen(NULL, my_wbscreen_ptr);
477 #ifdef __amigaos4__
478 		/* Performance on AmigaOS 4 is reported to be slow when run at 1280x960, so we set a max of 640x480.
479 		 */
480 		const int max_width_c = 640, max_height_c = 480;
481 		if( *user_width > max_width_c )
482 			*user_width = max_width_c;
483 		if( *user_height > max_height_c )
484 			*user_height = max_height_c;
485 		LOG("size restricted to %d x %d\n", *user_width, *user_height);
486 #endif
487 	}
488 }
489 
490 #endif
491