src/unix.cpp
changeset 5835 e0ff603ae0b7
parent 5827 20879afb77f6
child 5650 aefc131bf5ce
equal deleted inserted replaced
5834:7bf92d5a5a0f 5835:e0ff603ae0b7
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "functions.h"
       
     6 #include "window.h"
       
     7 #include "string.h"
       
     8 #include "table/strings.h"
       
     9 #include "variables.h"
       
    10 
       
    11 #include <dirent.h>
       
    12 #include <unistd.h>
       
    13 #include <sys/stat.h>
       
    14 #include <time.h>
       
    15 #include <signal.h>
       
    16 
       
    17 #ifdef USE_HOMEDIR
       
    18 #include <pwd.h>
       
    19 #endif
       
    20 
       
    21 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__)
       
    22 	#define HAS_STATVFS
       
    23 #endif
       
    24 
       
    25 #ifdef HAS_STATVFS
       
    26 #include <sys/statvfs.h>
       
    27 #endif
       
    28 
       
    29 
       
    30 #ifdef __MORPHOS__
       
    31 #include <exec/types.h>
       
    32 ULONG __stack = (1024*1024)*2; // maybe not that much is needed actually ;)
       
    33 
       
    34 // The system supplied definition of SIG_IGN does not match
       
    35 #undef SIG_IGN
       
    36 #define SIG_IGN (void (*)(int))1
       
    37 #endif /* __MORPHOS__ */
       
    38 
       
    39 #ifdef __AMIGA__
       
    40 #warning add stack symbol to avoid that user needs to set stack manually (tokai)
       
    41 // ULONG __stack =
       
    42 #endif
       
    43 
       
    44 #if defined(__APPLE__)
       
    45 	#if defined(WITH_SDL)
       
    46 		//the mac implementation needs this file included in the same file as main()
       
    47 		#include <SDL.h>
       
    48 	#endif
       
    49 #endif
       
    50 
       
    51 bool FiosIsRoot(const char *path)
       
    52 {
       
    53 #if !defined(__MORPHOS__) && !defined(__AMIGAOS__)
       
    54 	return path[1] == '\0';
       
    55 #else
       
    56 	/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
       
    57 	const char *s = strchr(path, ':');
       
    58 	return s[1] == '\0';
       
    59 #endif
       
    60 }
       
    61 
       
    62 void FiosGetDrives(void)
       
    63 {
       
    64 	return;
       
    65 }
       
    66 
       
    67 bool FiosGetDiskFreeSpace(const char *path, uint32 *tot)
       
    68 {
       
    69 	uint32 free = 0;
       
    70 
       
    71 #ifdef HAS_STATVFS
       
    72 	{
       
    73 		struct statvfs s;
       
    74 
       
    75 		if (statvfs(path, &s) != 0) return false;
       
    76 		free = (uint64)s.f_frsize * s.f_bavail >> 20;
       
    77 	}
       
    78 #endif
       
    79 	if (tot != NULL) *tot = free;
       
    80 	return true;
       
    81 }
       
    82 
       
    83 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
       
    84 {
       
    85 	char filename[MAX_PATH];
       
    86 
       
    87 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
       
    88 	/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
       
    89 	if (FiosIsRoot(path)) {
       
    90 		snprintf(filename, lengthof(filename), "%s:%s", path, ent->d_name);
       
    91 	} else // XXX - only next line!
       
    92 #endif
       
    93 	snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, ent->d_name);
       
    94 
       
    95 	if (stat(filename, sb) != 0) return false;
       
    96 
       
    97 	return (ent->d_name[0] != '.'); // hidden file
       
    98 }
       
    99 
       
   100 #if defined(__BEOS__) || defined(__linux__)
       
   101 static void ChangeWorkingDirectory(char *exe)
       
   102 {
       
   103 	char *s = strrchr(exe, '/');
       
   104 	if (s != NULL) {
       
   105 		*s = '\0';
       
   106 		chdir(exe);
       
   107 		*s = '/';
       
   108 	}
       
   109 }
       
   110 #endif
       
   111 
       
   112 void ShowInfo(const char *str)
       
   113 {
       
   114 	fprintf(stderr, str);
       
   115 }
       
   116 
       
   117 void ShowOSErrorBox(const char *buf)
       
   118 {
       
   119 #if defined(__APPLE__)
       
   120 	// this creates an NSAlertPanel with the contents of 'buf'
       
   121 	// this is the native and nicest way to do this on OSX
       
   122 	ShowMacDialog( buf, "See readme for more info\nMost likely you are missing files from the original TTD", "Quit" );
       
   123 #else
       
   124 	// all systems, but OSX
       
   125 	fprintf(stderr, "\033[1;31mError: %s\033[0;39m\n", buf);
       
   126 #endif
       
   127 }
       
   128 
       
   129 #ifdef WITH_COCOA
       
   130 void cocoaSetWorkingDirectory(void);
       
   131 void cocoaSetupAutoreleasePool(void);
       
   132 void cocoaReleaseAutoreleasePool(void);
       
   133 #endif
       
   134 
       
   135 int CDECL main(int argc, char* argv[])
       
   136 {
       
   137 	int ret;
       
   138 
       
   139 #ifdef WITH_COCOA
       
   140 	cocoaSetupAutoreleasePool();
       
   141 	/* This is passed if we are launched by double-clicking */
       
   142 	if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
       
   143 		argv[1] = NULL;
       
   144 		argc = 1;
       
   145 		cocoaSetWorkingDirectory();
       
   146 	}
       
   147 #endif
       
   148 
       
   149 	// change the working directory to enable doubleclicking in UIs
       
   150 #if defined(__BEOS__) || defined(__linux__)
       
   151 	ChangeWorkingDirectory(argv[0]);
       
   152 #endif
       
   153 
       
   154 	_random_seeds[1][1] = _random_seeds[1][0] = _random_seeds[0][1] = _random_seeds[0][0] = time(NULL);
       
   155 	SeedMT(_random_seeds[0][1]);
       
   156 
       
   157 	signal(SIGPIPE, SIG_IGN);
       
   158 
       
   159 	ret = ttd_main(argc, argv);
       
   160 
       
   161 #ifdef WITH_COCOA
       
   162 	cocoaReleaseAutoreleasePool();
       
   163 #endif
       
   164 
       
   165 	return ret;
       
   166 }
       
   167 
       
   168 void DeterminePaths(void)
       
   169 {
       
   170 	char *s;
       
   171 
       
   172 	_paths.game_data_dir = malloc(MAX_PATH);
       
   173 	ttd_strlcpy(_paths.game_data_dir, GAME_DATA_DIR, MAX_PATH);
       
   174 	#if defined SECOND_DATA_DIR
       
   175 	_paths.second_data_dir = malloc(MAX_PATH);
       
   176 	ttd_strlcpy(_paths.second_data_dir, SECOND_DATA_DIR, MAX_PATH);
       
   177 	#endif
       
   178 
       
   179 #if defined(USE_HOMEDIR)
       
   180 	{
       
   181 		const char *homedir = getenv("HOME");
       
   182 
       
   183 		if (homedir == NULL) {
       
   184 			const struct passwd *pw = getpwuid(getuid());
       
   185 			if (pw != NULL) homedir = pw->pw_dir;
       
   186 		}
       
   187 
       
   188 		_paths.personal_dir = str_fmt("%s" PATHSEP "%s", homedir, PERSONAL_DIR);
       
   189 	}
       
   190 
       
   191 #else /* not defined(USE_HOMEDIR) */
       
   192 
       
   193 	_paths.personal_dir = malloc(MAX_PATH);
       
   194 	ttd_strlcpy(_paths.personal_dir, PERSONAL_DIR, MAX_PATH);
       
   195 
       
   196 	// check if absolute or relative path
       
   197 	s = strchr(_paths.personal_dir, '/');
       
   198 
       
   199 	// add absolute path
       
   200 	if (s == NULL || _paths.personal_dir != s) {
       
   201 		getcwd(_paths.personal_dir, MAX_PATH);
       
   202 		s = strchr(_paths.personal_dir, 0);
       
   203 		*s++ = '/';
       
   204 		ttd_strlcpy(s, PERSONAL_DIR, MAX_PATH);
       
   205 	}
       
   206 
       
   207 #endif /* defined(USE_HOMEDIR) */
       
   208 
       
   209 	s = strchr(_paths.personal_dir, 0);
       
   210 
       
   211 	// append a / ?
       
   212 	if (s[-1] != '/') strcpy(s, "/");
       
   213 
       
   214 	_paths.save_dir = str_fmt("%ssave", _paths.personal_dir);
       
   215 	_paths.autosave_dir = str_fmt("%s/autosave", _paths.save_dir);
       
   216 	_paths.scenario_dir = str_fmt("%sscenario", _paths.personal_dir);
       
   217 	_paths.heightmap_dir = str_fmt("%sscenario/heightmap", _paths.personal_dir);
       
   218 	_paths.gm_dir = str_fmt("%sgm/", _paths.game_data_dir);
       
   219 	_paths.data_dir = str_fmt("%sdata/", _paths.game_data_dir);
       
   220 
       
   221 	if (_config_file == NULL)
       
   222 		_config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
       
   223 
       
   224 	_highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
       
   225 	_log_file = str_fmt("%sopenttd.log", _paths.personal_dir);
       
   226 
       
   227 #if defined CUSTOM_LANG_DIR
       
   228 	// sets the search path for lng files to the custom one
       
   229 	_paths.lang_dir = malloc( MAX_PATH );
       
   230 	ttd_strlcpy( _paths.lang_dir, CUSTOM_LANG_DIR, MAX_PATH);
       
   231 #else
       
   232 	_paths.lang_dir = str_fmt("%slang/", _paths.game_data_dir);
       
   233 #endif
       
   234 
       
   235 	// create necessary folders
       
   236 	mkdir(_paths.personal_dir, 0755);
       
   237 	mkdir(_paths.save_dir, 0755);
       
   238 	mkdir(_paths.autosave_dir, 0755);
       
   239 	mkdir(_paths.scenario_dir, 0755);
       
   240 	mkdir(_paths.heightmap_dir, 0755);
       
   241 }
       
   242 
       
   243 bool InsertTextBufferClipboard(Textbuf *tb)
       
   244 {
       
   245 	return false;
       
   246 }
       
   247 
       
   248 
       
   249 // multi os compatible sleep function
       
   250 
       
   251 #ifdef __AMIGA__
       
   252 // usleep() implementation
       
   253 #	include <devices/timer.h>
       
   254 #	include <dos/dos.h>
       
   255 
       
   256 	extern struct Device      *TimerBase    = NULL;
       
   257 	extern struct MsgPort     *TimerPort    = NULL;
       
   258 	extern struct timerequest *TimerRequest = NULL;
       
   259 #endif // __AMIGA__
       
   260 
       
   261 void CSleep(int milliseconds)
       
   262 {
       
   263 	#if !defined(__BEOS__) && !defined(__AMIGA__)
       
   264 		usleep(milliseconds * 1000);
       
   265 	#endif
       
   266 	#ifdef __BEOS__
       
   267 		snooze(milliseconds * 1000);
       
   268 	#endif
       
   269 	#if defined(__AMIGA__)
       
   270 	{
       
   271 		ULONG signals;
       
   272 		ULONG TimerSigBit = 1 << TimerPort->mp_SigBit;
       
   273 
       
   274 		// send IORequest
       
   275 		TimerRequest->tr_node.io_Command = TR_ADDREQUEST;
       
   276 		TimerRequest->tr_time.tv_secs    = (milliseconds * 1000) / 1000000;
       
   277 		TimerRequest->tr_time.tv_micro   = (milliseconds * 1000) % 1000000;
       
   278 		SendIO((struct IORequest *)TimerRequest);
       
   279 
       
   280 		if (!((signals = Wait(TimerSigBit | SIGBREAKF_CTRL_C)) & TimerSigBit) ) {
       
   281 			AbortIO((struct IORequest *)TimerRequest);
       
   282 		}
       
   283 		WaitIO((struct IORequest *)TimerRequest);
       
   284 	}
       
   285 	#endif // __AMIGA__
       
   286 }
       
   287 
       
   288 #ifdef WITH_ICONV
       
   289 
       
   290 #include <iconv.h>
       
   291 #include <errno.h>
       
   292 #include "debug.h"
       
   293 
       
   294 #define INTERNALCODE "UTF-8"
       
   295 
       
   296 /** Try and try to decipher the current locale from environmental
       
   297  * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable
       
   298  * locale can be found, don't do any conversion "" */
       
   299 static const char *GetLocalCode(void)
       
   300 {
       
   301 #if defined(__APPLE__)
       
   302 	return "UTF-8-MAC";
       
   303 #else
       
   304 	/* Strip locale (eg en_US.UTF-8) to only have UTF-8 */
       
   305 	const char *locale = GetCurrentLocale("LC_CTYPE");
       
   306 	if (locale != NULL) locale = strchr(locale, '.');
       
   307 
       
   308 	return (locale == NULL) ? "" : locale + 1;
       
   309 #endif
       
   310 }
       
   311 
       
   312 /** FYI: This is not thread-safe.
       
   313  * convert between locales, which from and which to is set in the calling
       
   314  * functions OTTD2FS() and FS2OTTD(). You should NOT use this function directly
       
   315  * NOTE: iconv was added in OSX 10.3. 10.2.x will still have the invalid char
       
   316  * issues. There aren't any easy fix for this */
       
   317 static const char *convert_tofrom_fs(iconv_t convd, const char *name)
       
   318 {
       
   319 	static char buf[1024];
       
   320 	/* Work around buggy iconv implementation where inbuf is wrongly typed as
       
   321 	 * non-const. Correct implementation is at
       
   322 	 * http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html */
       
   323 #if defined (__GLIBC__) || defined (__GNU_LIBRARY__)
       
   324 	char *inbuf = (char*)name;
       
   325 #else
       
   326 	const char *inbuf = name;
       
   327 #endif
       
   328 
       
   329 	char *outbuf  = buf;
       
   330 	size_t outlen = sizeof(buf) - 1;
       
   331 	size_t inlen  = strlen(name);
       
   332 
       
   333 	ttd_strlcpy(outbuf, name, sizeof(buf));
       
   334 
       
   335 	iconv(convd, NULL, NULL, NULL, NULL);
       
   336 	if (iconv(convd, &inbuf, &inlen, &outbuf, &outlen) == (size_t)(-1)) {
       
   337 		DEBUG(misc, 0, "[iconv] error converting '%s'. Errno %d", name, errno);
       
   338 	}
       
   339 
       
   340 	*outbuf = '\0';
       
   341 	// FIX: invalid characters will abort conversion, but they shouldn't occur?
       
   342 	return buf;
       
   343 }
       
   344 
       
   345 /** Convert from OpenTTD's encoding to that of the local environment
       
   346  * @param name pointer to a valid string that will be converted
       
   347  * @return pointer to a new stringbuffer that contains the converted string */
       
   348 const char *OTTD2FS(const char *name)
       
   349 {
       
   350 	static iconv_t convd = (iconv_t)(-1);
       
   351 
       
   352 	if (convd == (iconv_t)(-1)) {
       
   353 		const char *env = GetLocalCode();
       
   354 		convd = iconv_open(env, INTERNALCODE);
       
   355 		if (convd == (iconv_t)(-1)) {
       
   356 			DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env);
       
   357 			return name;
       
   358 		}
       
   359 	}
       
   360 
       
   361 	return convert_tofrom_fs(convd, name);
       
   362 }
       
   363 
       
   364 /** Convert to OpenTTD's encoding from that of the local environment
       
   365  * @param name pointer to a valid string that will be converted
       
   366  * @return pointer to a new stringbuffer that contains the converted string */
       
   367 const char *FS2OTTD(const char *name)
       
   368 {
       
   369 	static iconv_t convd = (iconv_t)(-1);
       
   370 
       
   371 	if (convd == (iconv_t)(-1)) {
       
   372 		const char *env = GetLocalCode();
       
   373 		convd = iconv_open(INTERNALCODE, env);
       
   374 		if (convd == (iconv_t)(-1)) {
       
   375 			DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE);
       
   376 			return name;
       
   377 		}
       
   378 	}
       
   379 
       
   380 	return convert_tofrom_fs(convd, name);
       
   381 }
       
   382 
       
   383 #else
       
   384 const char *FS2OTTD(const char *name) {return name;}
       
   385 const char *OTTD2FS(const char *name) {return name;}
       
   386 #endif /* WITH_ICONV */