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