File: | Platform.c |
Warning: | line 674, column 2 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include "Platform.h" | |||
2 | #include "String.h" | |||
3 | #include "Logger.h" | |||
4 | #include "Stream.h" | |||
5 | #include "ExtMath.h" | |||
6 | #include "Drawer2D.h" | |||
7 | #include "Funcs.h" | |||
8 | #include "Window.h" | |||
9 | #include "Utils.h" | |||
10 | #include "Errors.h" | |||
11 | ||||
12 | #if defined CC_BUILD_WIN | |||
13 | #define WIN32_LEAN_AND_MEAN | |||
14 | #define NOSERVICE | |||
15 | #define NOMCX | |||
16 | #define NOIME | |||
17 | #ifndef UNICODE | |||
18 | #define UNICODE | |||
19 | #define _UNICODE | |||
20 | #endif | |||
21 | ||||
22 | #include <windows.h> | |||
23 | #include <winsock2.h> | |||
24 | #include <ws2tcpip.h> | |||
25 | #include <shellapi.h> | |||
26 | #include <wincrypt.h> | |||
27 | ||||
28 | #define Socket__Error()(*__errno()) WSAGetLastError() | |||
29 | static HANDLE heap; | |||
30 | ||||
31 | const cc_result ReturnCode_FileShareViolation = ERROR_SHARING_VIOLATION; | |||
32 | const cc_result ReturnCode_FileNotFound = ERROR_FILE_NOT_FOUND; | |||
33 | const cc_result ReturnCode_SocketInProgess = WSAEINPROGRESS; | |||
34 | const cc_result ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK; | |||
35 | #elif defined CC_BUILD_POSIX | |||
36 | /* POSIX can be shared between Linux/BSD/macOS */ | |||
37 | #include <errno(*__errno()).h> | |||
38 | #include <time.h> | |||
39 | #include <stdlib.h> | |||
40 | #include <string.h> | |||
41 | #include <unistd.h> | |||
42 | #include <dirent.h> | |||
43 | #include <fcntl.h> | |||
44 | #include <pthread.h> | |||
45 | #include <arpa/inet.h> | |||
46 | #include <netinet/in.h> | |||
47 | #include <sys/socket.h> | |||
48 | #include <sys/ioctl.h> | |||
49 | #include <sys/types.h> | |||
50 | #include <sys/stat.h> | |||
51 | #include <sys/time.h> | |||
52 | #include <utime.h> | |||
53 | #include <signal.h> | |||
54 | #include <stdio.h> | |||
55 | ||||
56 | #define Socket__Error()(*__errno()) errno(*__errno()) | |||
57 | static char* defaultDirectory; | |||
58 | const cc_result ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */ | |||
59 | const cc_result ReturnCode_FileNotFound = ENOENT2; | |||
60 | const cc_result ReturnCode_SocketInProgess = EINPROGRESS36; | |||
61 | const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK35; | |||
62 | #endif | |||
63 | /* Platform specific include files (Try to share for UNIX-ish) */ | |||
64 | #if defined CC_BUILD_OSX | |||
65 | #include <mach/mach_time.h> | |||
66 | #include <mach-o/dyld.h> | |||
67 | #include <ApplicationServices/ApplicationServices.h> | |||
68 | #elif defined CC_BUILD_SOLARIS | |||
69 | #include <sys/filio.h> | |||
70 | #elif defined CC_BUILD_BSD | |||
71 | #include <sys/sysctl.h> | |||
72 | #elif defined CC_BUILD_HAIKU | |||
73 | /* TODO: Use load_image/resume_thread instead of fork */ | |||
74 | /* Otherwise opening browser never works because fork fails */ | |||
75 | #include <kernel/image.h> | |||
76 | #elif defined CC_BUILD_WEB | |||
77 | #include <emscripten.h> | |||
78 | #include "Chat.h" | |||
79 | #endif | |||
80 | ||||
81 | ||||
82 | /*########################################################################################################################* | |||
83 | *---------------------------------------------------------Memory----------------------------------------------------------* | |||
84 | *#########################################################################################################################*/ | |||
85 | void Mem_Set(void* dst, cc_uint8 value, cc_uint32 numBytes) { memset(dst, value, numBytes); } | |||
86 | void Mem_Copy(void* dst, const void* src, cc_uint32 numBytes) { memcpy(dst, src, numBytes); } | |||
87 | ||||
88 | CC_NOINLINE__attribute__((noinline)) static void AbortOnAllocFailed(const char* place) { | |||
89 | cc_string log; char logBuffer[STRING_SIZE64+20 + 1]; | |||
90 | String_InitArray_NT(log, logBuffer)log.buffer = logBuffer; log.length = 0; log.capacity = sizeof (logBuffer) - 1;; | |||
91 | ||||
92 | String_Format1(&log, "Out of memory! (when allocating %c)", place); | |||
93 | log.buffer[log.length] = '\0'; | |||
94 | Logger_Abort(log.buffer); | |||
95 | } | |||
96 | ||||
97 | void* Mem_Alloc(cc_uint32 numElems, cc_uint32 elemsSize, const char* place) { | |||
98 | void* ptr = Mem_TryAlloc(numElems, elemsSize); | |||
99 | if (!ptr) AbortOnAllocFailed(place); | |||
100 | return ptr; | |||
101 | } | |||
102 | ||||
103 | void* Mem_AllocCleared(cc_uint32 numElems, cc_uint32 elemsSize, const char* place) { | |||
104 | void* ptr = Mem_TryAllocCleared(numElems, elemsSize); | |||
105 | if (!ptr) AbortOnAllocFailed(place); | |||
106 | return ptr; | |||
107 | } | |||
108 | ||||
109 | void* Mem_Realloc(void* mem, cc_uint32 numElems, cc_uint32 elemsSize, const char* place) { | |||
110 | void* ptr = Mem_TryRealloc(mem, numElems, elemsSize); | |||
111 | if (!ptr) AbortOnAllocFailed(place); | |||
112 | return ptr; | |||
113 | } | |||
114 | ||||
115 | static cc_uint32 CalcMemSize(cc_uint32 numElems, cc_uint32 elemsSize) { | |||
116 | cc_uint32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ | |||
117 | if (numBytes < numElems) return 0; /* TODO: Use proper overflow checking */ | |||
118 | return numBytes; | |||
119 | } | |||
120 | ||||
121 | #if defined CC_BUILD_WIN | |||
122 | void* Mem_TryAlloc(cc_uint32 numElems, cc_uint32 elemsSize) { | |||
123 | cc_uint32 size = CalcMemSize(numElems, elemsSize); | |||
124 | return size ? HeapAlloc(heap, 0, size) : NULL((void*)0); | |||
125 | } | |||
126 | ||||
127 | void* Mem_TryAllocCleared(cc_uint32 numElems, cc_uint32 elemsSize) { | |||
128 | cc_uint32 size = CalcMemSize(numElems, elemsSize); | |||
129 | return size ? HeapAlloc(heap, HEAP_ZERO_MEMORY, size) : NULL((void*)0); | |||
130 | } | |||
131 | ||||
132 | void* Mem_TryRealloc(void* mem, cc_uint32 numElems, cc_uint32 elemsSize) { | |||
133 | cc_uint32 size = CalcMemSize(numElems, elemsSize); | |||
134 | return size ? HeapReAlloc(heap, 0, mem, size) : NULL((void*)0); | |||
135 | } | |||
136 | ||||
137 | void Mem_Free(void* mem) { | |||
138 | if (mem) HeapFree(heap, 0, mem); | |||
139 | } | |||
140 | #elif defined CC_BUILD_POSIX | |||
141 | void* Mem_TryAlloc(cc_uint32 numElems, cc_uint32 elemsSize) { | |||
142 | cc_uint32 size = CalcMemSize(numElems, elemsSize); | |||
143 | return size
| |||
144 | } | |||
145 | ||||
146 | void* Mem_TryAllocCleared(cc_uint32 numElems, cc_uint32 elemsSize) { | |||
147 | return calloc(numElems, elemsSize); | |||
148 | } | |||
149 | ||||
150 | void* Mem_TryRealloc(void* mem, cc_uint32 numElems, cc_uint32 elemsSize) { | |||
151 | cc_uint32 size = CalcMemSize(numElems, elemsSize); | |||
152 | return size ? realloc(mem, size) : NULL((void*)0); | |||
153 | } | |||
154 | ||||
155 | void Mem_Free(void* mem) { | |||
156 | if (mem
| |||
157 | } | |||
158 | #endif | |||
159 | ||||
160 | ||||
161 | /*########################################################################################################################* | |||
162 | *------------------------------------------------------Logging/Time-------------------------------------------------------* | |||
163 | *#########################################################################################################################*/ | |||
164 | void Platform_Log1(const char* format, const void* a1) { | |||
165 | Platform_Log4(format, a1, NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
166 | } | |||
167 | void Platform_Log2(const char* format, const void* a1, const void* a2) { | |||
168 | Platform_Log4(format, a1, a2, NULL((void*)0), NULL((void*)0)); | |||
169 | } | |||
170 | void Platform_Log3(const char* format, const void* a1, const void* a2, const void* a3) { | |||
171 | Platform_Log4(format, a1, a2, a3, NULL((void*)0)); | |||
172 | } | |||
173 | ||||
174 | void Platform_Log4(const char* format, const void* a1, const void* a2, const void* a3, const void* a4) { | |||
175 | cc_string msg; char msgBuffer[512]; | |||
176 | String_InitArray(msg, msgBuffer)msg.buffer = msgBuffer; msg.length = 0; msg.capacity = sizeof (msgBuffer);; | |||
177 | ||||
178 | String_Format4(&msg, format, a1, a2, a3, a4); | |||
179 | Platform_Log(msg.buffer, msg.length); | |||
180 | } | |||
181 | ||||
182 | void Platform_LogConst(const char* message) { | |||
183 | Platform_Log(message, String_Length(message)); | |||
184 | } | |||
185 | ||||
186 | /* TODO: check this is actually accurate */ | |||
187 | static cc_uint64 sw_freqMul = 1, sw_freqDiv = 1; | |||
188 | cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { | |||
189 | if (end < beg) return 0; | |||
190 | return ((end - beg) * sw_freqMul) / sw_freqDiv; | |||
191 | } | |||
192 | ||||
193 | cc_uint64 Stopwatch_ElapsedMilliseconds(cc_uint64 beg, cc_uint64 end) { | |||
194 | return Stopwatch_ElapsedMicroseconds(beg, end) / 1000; | |||
195 | } | |||
196 | ||||
197 | #if defined CC_BUILD_WIN | |||
198 | static HANDLE conHandle; | |||
199 | static BOOL hasDebugger; | |||
200 | ||||
201 | void Platform_Log(const char* msg, int len) { | |||
202 | char tmp[2048 + 1]; | |||
203 | DWORD wrote; | |||
204 | ||||
205 | if (conHandle) { | |||
206 | WriteFile(conHandle, msg, len, &wrote, NULL((void*)0)); | |||
207 | WriteFile(conHandle, "\n", 1, &wrote, NULL((void*)0)); | |||
208 | } | |||
209 | ||||
210 | if (!hasDebugger) return; | |||
211 | len = min(len, 2048)((len) < (2048) ? (len) : (2048)); | |||
212 | Mem_Copy(tmp, msg, len); tmp[len] = '\0'; | |||
213 | ||||
214 | OutputDebugStringA(tmp); | |||
215 | OutputDebugStringA("\n"); | |||
216 | } | |||
217 | ||||
218 | #define FILETIME_EPOCH 50491123200000ULL | |||
219 | #define FILETIME_UNIX_EPOCH 11644473600LL | |||
220 | #define FileTime_TotalMS(time) ((time / 10000) + FILETIME_EPOCH) | |||
221 | #define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) | |||
222 | TimeMS DateTime_CurrentUTC_MS(void) { | |||
223 | FILETIME ft; | |||
224 | cc_uint64 raw; | |||
225 | ||||
226 | GetSystemTimeAsFileTime(&ft); | |||
227 | /* in 100 nanosecond units, since Jan 1 1601 */ | |||
228 | raw = ft.dwLowDateTime | ((cc_uint64)ft.dwHighDateTime << 32); | |||
229 | return FileTime_TotalMS(raw); | |||
230 | } | |||
231 | ||||
232 | void DateTime_CurrentLocal(struct DateTime* t) { | |||
233 | SYSTEMTIME localTime; | |||
234 | GetLocalTime(&localTime); | |||
235 | ||||
236 | t->year = localTime.wYear; | |||
237 | t->month = localTime.wMonth; | |||
238 | t->day = localTime.wDay; | |||
239 | t->hour = localTime.wHour; | |||
240 | t->minute = localTime.wMinute; | |||
241 | t->second = localTime.wSecond; | |||
242 | } | |||
243 | ||||
244 | static cc_bool sw_highRes; | |||
245 | cc_uint64 Stopwatch_Measure(void) { | |||
246 | LARGE_INTEGER t; | |||
247 | FILETIME ft; | |||
248 | ||||
249 | if (sw_highRes) { | |||
250 | QueryPerformanceCounter(&t); | |||
251 | return (cc_uint64)t.QuadPart; | |||
252 | } else { | |||
253 | GetSystemTimeAsFileTime(&ft); | |||
254 | return (cc_uint64)ft.dwLowDateTime | ((cc_uint64)ft.dwHighDateTime << 32); | |||
255 | } | |||
256 | } | |||
257 | #elif defined CC_BUILD_POSIX | |||
258 | /* log to android logcat */ | |||
259 | #ifdef CC_BUILD_ANDROID | |||
260 | #include <android/log.h> | |||
261 | void Platform_Log(const char* msg, int len) { | |||
262 | char tmp[2048 + 1]; | |||
263 | len = min(len, 2048)((len) < (2048) ? (len) : (2048)); | |||
264 | ||||
265 | Mem_Copy(tmp, msg, len); tmp[len] = '\0'; | |||
266 | __android_log_write(ANDROID_LOG_DEBUG, "ClassiCube", tmp); | |||
267 | } | |||
268 | #else | |||
269 | void Platform_Log(const char* msg, int len) { | |||
270 | write(STDOUT_FILENO1, msg, len); | |||
271 | write(STDOUT_FILENO1, "\n", 1); | |||
272 | } | |||
273 | #endif | |||
274 | ||||
275 | #define UnixTime_TotalMS(time)((cc_uint64)time.tv_sec * 1000 + 62135596800000ULL + (time.tv_usec / 1000)) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH62135596800000ULL + (time.tv_usec / 1000)) | |||
276 | TimeMS DateTime_CurrentUTC_MS(void) { | |||
277 | struct timeval cur; | |||
278 | gettimeofday(&cur, NULL((void*)0)); | |||
279 | return UnixTime_TotalMS(cur)((cc_uint64)cur.tv_sec * 1000 + 62135596800000ULL + (cur.tv_usec / 1000)); | |||
280 | } | |||
281 | ||||
282 | void DateTime_CurrentLocal(struct DateTime* t) { | |||
283 | struct timeval cur; | |||
284 | struct tm loc_time; | |||
285 | gettimeofday(&cur, NULL((void*)0)); | |||
286 | localtime_r(&cur.tv_sec, &loc_time); | |||
287 | ||||
288 | t->year = loc_time.tm_year + 1900; | |||
289 | t->month = loc_time.tm_mon + 1; | |||
290 | t->day = loc_time.tm_mday; | |||
291 | t->hour = loc_time.tm_hour; | |||
292 | t->minute = loc_time.tm_min; | |||
293 | t->second = loc_time.tm_sec; | |||
294 | } | |||
295 | ||||
296 | #define NS_PER_SEC1000000000ULL 1000000000ULL | |||
297 | #endif | |||
298 | /* clock_gettime is optional, see http://pubs.opengroup.org/onlinepubs/009696899/functions/clock_getres.html */ | |||
299 | /* "... These functions are part of the Timers option and need not be available on all implementations..." */ | |||
300 | #if defined CC_BUILD_WEB | |||
301 | cc_uint64 Stopwatch_Measure(void) { | |||
302 | /* time is a milliseconds double */ | |||
303 | return (cc_uint64)(emscripten_get_now() * 1000); | |||
304 | } | |||
305 | #elif defined CC_BUILD_OSX | |||
306 | cc_uint64 Stopwatch_Measure(void) { return mach_absolute_time(); } | |||
307 | #elif defined CC_BUILD_SOLARIS | |||
308 | cc_uint64 Stopwatch_Measure(void) { return gethrtime(); } | |||
309 | #elif defined CC_BUILD_POSIX | |||
310 | cc_uint64 Stopwatch_Measure(void) { | |||
311 | struct timespec t; | |||
312 | /* TODO: CLOCK_MONOTONIC_RAW ?? */ | |||
313 | clock_gettime(CLOCK_MONOTONIC3, &t); | |||
314 | return (cc_uint64)t.tv_sec * NS_PER_SEC1000000000ULL + t.tv_nsec; | |||
315 | } | |||
316 | #endif | |||
317 | ||||
318 | ||||
319 | /*########################################################################################################################* | |||
320 | *-----------------------------------------------------Directory/File------------------------------------------------------* | |||
321 | *#########################################################################################################################*/ | |||
322 | #if defined CC_BUILD_WIN | |||
323 | int Directory_Exists(const cc_string* path) { | |||
324 | TCHAR str[NATIVE_STR_LEN600]; | |||
325 | DWORD attribs; | |||
326 | ||||
327 | Platform_EncodeString(str, path); | |||
328 | attribs = GetFileAttributes(str); | |||
329 | return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY); | |||
330 | } | |||
331 | ||||
332 | cc_result Directory_Create(const cc_string* path) { | |||
333 | TCHAR str[NATIVE_STR_LEN600]; | |||
334 | BOOL success; | |||
335 | ||||
336 | Platform_EncodeString(str, path); | |||
337 | success = CreateDirectory(str, NULL((void*)0)); | |||
338 | return success ? 0 : GetLastError(); | |||
339 | } | |||
340 | ||||
341 | int File_Exists(const cc_string* path) { | |||
342 | TCHAR str[NATIVE_STR_LEN600]; | |||
343 | DWORD attribs; | |||
344 | ||||
345 | Platform_EncodeString(str, path); | |||
346 | attribs = GetFileAttributes(str); | |||
347 | return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY); | |||
348 | } | |||
349 | ||||
350 | cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { | |||
351 | cc_string path; char pathBuffer[MAX_PATH + 10]; | |||
352 | TCHAR str[NATIVE_STR_LEN600]; | |||
353 | TCHAR* src; | |||
354 | WIN32_FIND_DATA entry; | |||
355 | HANDLE find; | |||
356 | cc_result res; | |||
357 | int i; | |||
358 | ||||
359 | /* Need to append \* to search for files in directory */ | |||
360 | String_InitArray(path, pathBuffer)path.buffer = pathBuffer; path.length = 0; path.capacity = sizeof (pathBuffer);; | |||
361 | String_Format1(&path, "%s\\*", dirPath); | |||
362 | Platform_EncodeString(str, &path); | |||
363 | ||||
364 | find = FindFirstFile(str, &entry); | |||
365 | if (find == INVALID_HANDLE_VALUE) return GetLastError(); | |||
366 | ||||
367 | do { | |||
368 | path.length = 0; | |||
369 | String_Format1(&path, "%s/", dirPath); | |||
370 | ||||
371 | /* ignore . and .. entry */ | |||
372 | src = entry.cFileName; | |||
373 | if (src[0] == '.' && src[1] == '\0') continue; | |||
374 | if (src[0] == '.' && src[1] == '.' && src[2] == '\0') continue; | |||
375 | ||||
376 | for (i = 0; i < MAX_PATH && src[i]; i++) { | |||
377 | /* TODO: UTF16 to codepoint conversion */ | |||
378 | String_Append(&path, Convert_CodepointToCP437(src[i])); | |||
379 | } | |||
380 | ||||
381 | if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |||
382 | res = Directory_Enum(&path, obj, callback); | |||
383 | if (res) { FindClose(find); return res; } | |||
384 | } else { | |||
385 | callback(&path, obj); | |||
386 | } | |||
387 | } while (FindNextFile(find, &entry)); | |||
388 | ||||
389 | res = GetLastError(); /* return code from FindNextFile */ | |||
390 | FindClose(find); | |||
391 | return res == ERROR_NO_MORE_FILES ? 0 : GetLastError(); | |||
392 | } | |||
393 | ||||
394 | static cc_result File_Do(cc_file* file, const cc_string* path, DWORD access, DWORD createMode) { | |||
395 | TCHAR str[NATIVE_STR_LEN600]; | |||
396 | Platform_EncodeString(str, path); | |||
397 | *file = CreateFile(str, access, FILE_SHARE_READ, NULL((void*)0), createMode, 0, NULL((void*)0)); | |||
398 | return *file != INVALID_HANDLE_VALUE ? 0 : GetLastError(); | |||
399 | } | |||
400 | ||||
401 | cc_result File_Open(cc_file* file, const cc_string* path) { | |||
402 | return File_Do(file, path, GENERIC_READ, OPEN_EXISTING); | |||
403 | } | |||
404 | cc_result File_Create(cc_file* file, const cc_string* path) { | |||
405 | return File_Do(file, path, GENERIC_WRITE | GENERIC_READ, CREATE_ALWAYS); | |||
406 | } | |||
407 | cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { | |||
408 | return File_Do(file, path, GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS); | |||
409 | } | |||
410 | ||||
411 | cc_result File_Read(cc_file file, cc_uint8* data, cc_uint32 count, cc_uint32* bytesRead) { | |||
412 | BOOL success = ReadFile(file, data, count, bytesRead, NULL((void*)0)); | |||
413 | return success ? 0 : GetLastError(); | |||
414 | } | |||
415 | ||||
416 | cc_result File_Write(cc_file file, const cc_uint8* data, cc_uint32 count, cc_uint32* bytesWrote) { | |||
417 | BOOL success = WriteFile(file, data, count, bytesWrote, NULL((void*)0)); | |||
418 | return success ? 0 : GetLastError(); | |||
419 | } | |||
420 | ||||
421 | cc_result File_Close(cc_file file) { | |||
422 | return CloseHandle(file) ? 0 : GetLastError(); | |||
423 | } | |||
424 | ||||
425 | cc_result File_Seek(cc_file file, int offset, int seekType) { | |||
426 | static cc_uint8 modes[3] = { FILE_BEGIN, FILE_CURRENT, FILE_END }; | |||
427 | DWORD pos = SetFilePointer(file, offset, NULL((void*)0), modes[seekType]); | |||
428 | return pos != INVALID_SET_FILE_POINTER ? 0 : GetLastError(); | |||
429 | } | |||
430 | ||||
431 | cc_result File_Position(cc_file file, cc_uint32* pos) { | |||
432 | *pos = SetFilePointer(file, 0, NULL((void*)0), FILE_CURRENT); | |||
433 | return *pos != INVALID_SET_FILE_POINTER ? 0 : GetLastError(); | |||
434 | } | |||
435 | ||||
436 | cc_result File_Length(cc_file file, cc_uint32* len) { | |||
437 | *len = GetFileSize(file, NULL((void*)0)); | |||
438 | return *len != INVALID_FILE_SIZE ? 0 : GetLastError(); | |||
439 | } | |||
440 | #elif defined CC_BUILD_POSIX | |||
441 | int Directory_Exists(const cc_string* path) { | |||
442 | char str[NATIVE_STR_LEN600]; | |||
443 | struct stat sb; | |||
444 | Platform_EncodeString(str, path); | |||
445 | return stat(str, &sb) == 0 && S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000); | |||
446 | } | |||
447 | ||||
448 | cc_result Directory_Create(const cc_string* path) { | |||
449 | char str[NATIVE_STR_LEN600]; | |||
450 | Platform_EncodeString(str, path); | |||
451 | /* read/write/search permissions for owner and group, and with read/search permissions for others. */ | |||
452 | /* TODO: Is the default mode in all cases */ | |||
453 | return mkdir(str, S_IRWXU0000700 | S_IRWXG0000070 | S_IROTH0000004 | S_IXOTH0000001) == -1 ? errno(*__errno()) : 0; | |||
454 | } | |||
455 | ||||
456 | int File_Exists(const cc_string* path) { | |||
457 | char str[NATIVE_STR_LEN600]; | |||
458 | struct stat sb; | |||
459 | Platform_EncodeString(str, path); | |||
460 | return stat(str, &sb) == 0 && S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000); | |||
461 | } | |||
462 | ||||
463 | cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { | |||
464 | cc_string path; char pathBuffer[FILENAME_SIZE260]; | |||
465 | char str[NATIVE_STR_LEN600]; | |||
466 | DIR* dirPtr; | |||
467 | struct dirent* entry; | |||
468 | char* src; | |||
469 | int len, res; | |||
470 | ||||
471 | Platform_EncodeString(str, dirPath); | |||
472 | dirPtr = opendir(str); | |||
473 | if (!dirPtr) return errno(*__errno()); | |||
474 | ||||
475 | /* POSIX docs: "When the end of the directory is encountered, a null pointer is returned and errno is not changed." */ | |||
476 | /* errno is sometimes leftover from previous calls, so always reset it before readdir gets called */ | |||
477 | errno(*__errno()) = 0; | |||
478 | String_InitArray(path, pathBuffer)path.buffer = pathBuffer; path.length = 0; path.capacity = sizeof (pathBuffer);; | |||
479 | ||||
480 | while ((entry = readdir(dirPtr))) { | |||
481 | path.length = 0; | |||
482 | String_Format1(&path, "%s/", dirPath); | |||
483 | ||||
484 | /* ignore . and .. entry */ | |||
485 | src = entry->d_name; | |||
486 | if (src[0] == '.' && src[1] == '\0') continue; | |||
487 | if (src[0] == '.' && src[1] == '.' && src[2] == '\0') continue; | |||
488 | ||||
489 | len = String_Length(src); | |||
490 | Platform_DecodeString(&path, src, len); | |||
491 | ||||
492 | /* TODO: fallback to stat when this fails */ | |||
493 | if (entry->d_type == DT_DIR4) { | |||
494 | res = Directory_Enum(&path, obj, callback); | |||
495 | if (res) { closedir(dirPtr); return res; } | |||
496 | } else { | |||
497 | callback(&path, obj); | |||
498 | } | |||
499 | errno(*__errno()) = 0; | |||
500 | } | |||
501 | ||||
502 | res = errno(*__errno()); /* return code from readdir */ | |||
503 | closedir(dirPtr); | |||
504 | return res; | |||
505 | } | |||
506 | ||||
507 | static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { | |||
508 | char str[NATIVE_STR_LEN600]; | |||
509 | Platform_EncodeString(str, path); | |||
510 | *file = open(str, mode, S_IRUSR0000400 | S_IWUSR0000200 | S_IRGRP0000040 | S_IROTH0000004); | |||
511 | return *file == -1 ? errno(*__errno()) : 0; | |||
512 | } | |||
513 | ||||
514 | cc_result File_Open(cc_file* file, const cc_string* path) { | |||
515 | return File_Do(file, path, O_RDONLY0x0000); | |||
516 | } | |||
517 | cc_result File_Create(cc_file* file, const cc_string* path) { | |||
518 | return File_Do(file, path, O_RDWR0x0002 | O_CREAT0x0200 | O_TRUNC0x0400); | |||
519 | } | |||
520 | cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { | |||
521 | return File_Do(file, path, O_RDWR0x0002 | O_CREAT0x0200); | |||
522 | } | |||
523 | ||||
524 | cc_result File_Read(cc_file file, cc_uint8* data, cc_uint32 count, cc_uint32* bytesRead) { | |||
525 | *bytesRead = read(file, data, count); | |||
526 | return *bytesRead == -1 ? errno(*__errno()) : 0; | |||
527 | } | |||
528 | ||||
529 | cc_result File_Write(cc_file file, const cc_uint8* data, cc_uint32 count, cc_uint32* bytesWrote) { | |||
530 | *bytesWrote = write(file, data, count); | |||
531 | return *bytesWrote == -1 ? errno(*__errno()) : 0; | |||
532 | } | |||
533 | ||||
534 | cc_result File_Close(cc_file file) { | |||
535 | #ifndef CC_BUILD_WEB | |||
536 | return close(file) == -1 ? errno(*__errno()) : 0; | |||
537 | #else | |||
538 | int ret = close(file) == -1 ? errno(*__errno()) : 0; | |||
539 | EM_ASM( FS.syncfs(false0, function(err) { if (err) console.log(err); }); ); | |||
540 | return ret; | |||
541 | #endif | |||
542 | } | |||
543 | ||||
544 | cc_result File_Seek(cc_file file, int offset, int seekType) { | |||
545 | static cc_uint8 modes[3] = { SEEK_SET0, SEEK_CUR1, SEEK_END2 }; | |||
546 | return lseek(file, offset, modes[seekType]) == -1 ? errno(*__errno()) : 0; | |||
547 | } | |||
548 | ||||
549 | cc_result File_Position(cc_file file, cc_uint32* pos) { | |||
550 | *pos = lseek(file, 0, SEEK_CUR1); | |||
551 | return *pos == -1 ? errno(*__errno()) : 0; | |||
552 | } | |||
553 | ||||
554 | cc_result File_Length(cc_file file, cc_uint32* len) { | |||
555 | struct stat st; | |||
556 | if (fstat(file, &st) == -1) { *len = -1; return errno(*__errno()); } | |||
557 | *len = st.st_size; return 0; | |||
558 | } | |||
559 | #endif | |||
560 | ||||
561 | ||||
562 | /*########################################################################################################################* | |||
563 | *--------------------------------------------------------Threading--------------------------------------------------------* | |||
564 | *#########################################################################################################################*/ | |||
565 | #if defined CC_BUILD_WIN | |||
566 | void Thread_Sleep(cc_uint32 milliseconds) { Sleep(milliseconds); } | |||
567 | static DWORD WINAPI ExecThread(void* param) { | |||
568 | Thread_StartFunc func = (Thread_StartFunc)param; | |||
569 | func(); | |||
570 | return 0; | |||
571 | } | |||
572 | ||||
573 | void* Thread_Start(Thread_StartFunc func, cc_bool detach) { | |||
574 | DWORD threadID; | |||
575 | void* handle = CreateThread(NULL((void*)0), 0, ExecThread, (void*)func, 0, &threadID); | |||
576 | if (!handle) { | |||
577 | Logger_Abort2(GetLastError(), "Creating thread"); | |||
578 | } | |||
579 | ||||
580 | if (detach) Thread_Detach(handle); | |||
581 | return handle; | |||
582 | } | |||
583 | ||||
584 | void Thread_Detach(void* handle) { | |||
585 | if (!CloseHandle((HANDLE)handle)) { | |||
586 | Logger_Abort2(GetLastError(), "Freeing thread handle"); | |||
587 | } | |||
588 | } | |||
589 | ||||
590 | void Thread_Join(void* handle) { | |||
591 | WaitForSingleObject((HANDLE)handle, INFINITE); | |||
592 | Thread_Detach(handle); | |||
593 | } | |||
594 | ||||
595 | void* Mutex_Create(void) { | |||
596 | CRITICAL_SECTION* ptr = (CRITICAL_SECTION*)Mem_Alloc(1, sizeof(CRITICAL_SECTION), "mutex"); | |||
597 | InitializeCriticalSection(ptr); | |||
598 | return ptr; | |||
599 | } | |||
600 | ||||
601 | void Mutex_Free(void* handle) { | |||
602 | DeleteCriticalSection((CRITICAL_SECTION*)handle); | |||
603 | Mem_Free(handle); | |||
604 | } | |||
605 | void Mutex_Lock(void* handle) { EnterCriticalSection((CRITICAL_SECTION*)handle); } | |||
606 | void Mutex_Unlock(void* handle) { LeaveCriticalSection((CRITICAL_SECTION*)handle); } | |||
607 | ||||
608 | void* Waitable_Create(void) { | |||
609 | void* handle = CreateEvent(NULL((void*)0), false0, false0, NULL((void*)0)); | |||
610 | if (!handle) { | |||
611 | Logger_Abort2(GetLastError(), "Creating waitable"); | |||
612 | } | |||
613 | return handle; | |||
614 | } | |||
615 | ||||
616 | void Waitable_Free(void* handle) { | |||
617 | if (!CloseHandle((HANDLE)handle)) { | |||
618 | Logger_Abort2(GetLastError(), "Freeing waitable"); | |||
619 | } | |||
620 | } | |||
621 | ||||
622 | void Waitable_Signal(void* handle) { SetEvent((HANDLE)handle); } | |||
623 | void Waitable_Wait(void* handle) { | |||
624 | WaitForSingleObject((HANDLE)handle, INFINITE); | |||
625 | } | |||
626 | ||||
627 | void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { | |||
628 | WaitForSingleObject((HANDLE)handle, milliseconds); | |||
629 | } | |||
630 | #elif defined CC_BUILD_WEB | |||
631 | /* No real threading support with emscripten backend */ | |||
632 | void Thread_Sleep(cc_uint32 milliseconds) { } | |||
633 | void* Thread_Start(Thread_StartFunc func, cc_bool detach) { func(); return NULL((void*)0); } | |||
634 | void Thread_Detach(void* handle) { } | |||
635 | void Thread_Join(void* handle) { } | |||
636 | ||||
637 | void* Mutex_Create(void) { return NULL((void*)0); } | |||
638 | void Mutex_Free(void* handle) { } | |||
639 | void Mutex_Lock(void* handle) { } | |||
640 | void Mutex_Unlock(void* handle) { } | |||
641 | ||||
642 | void* Waitable_Create(void) { return NULL((void*)0); } | |||
643 | void Waitable_Free(void* handle) { } | |||
644 | void Waitable_Signal(void* handle) { } | |||
645 | void Waitable_Wait(void* handle) { } | |||
646 | void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { } | |||
647 | #elif defined CC_BUILD_POSIX | |||
648 | void Thread_Sleep(cc_uint32 milliseconds) { usleep(milliseconds * 1000); } | |||
649 | ||||
650 | #ifdef CC_BUILD_ANDROID | |||
651 | /* All threads using JNI must detach BEFORE they exit */ | |||
652 | /* (see https://developer.android.com/training/articles/perf-jni */ | |||
653 | static void* ExecThread(void* param) { | |||
654 | JNIEnv* env; | |||
655 | JavaGetCurrentEnv(env); | |||
656 | ||||
657 | ((Thread_StartFunc)param)(); | |||
658 | (*VM_Ptr)->DetachCurrentThread(VM_Ptr); | |||
659 | return NULL((void*)0); | |||
660 | } | |||
661 | #else | |||
662 | static void* ExecThread(void* param) { | |||
663 | ((Thread_StartFunc)param)(); | |||
664 | return NULL((void*)0); | |||
665 | } | |||
666 | #endif | |||
667 | ||||
668 | void* Thread_Start(Thread_StartFunc func, cc_bool detach) { | |||
669 | pthread_t* ptr = (pthread_t*)Mem_Alloc(1, sizeof(pthread_t), "thread"); | |||
| ||||
670 | int res = pthread_create(ptr, NULL((void*)0), ExecThread, (void*)func); | |||
671 | if (res) Logger_Abort2(res, "Creating thread"); | |||
672 | ||||
673 | if (detach) Thread_Detach(ptr); | |||
674 | return ptr; | |||
| ||||
675 | } | |||
676 | ||||
677 | void Thread_Detach(void* handle) { | |||
678 | pthread_t* ptr = (pthread_t*)handle; | |||
679 | int res = pthread_detach(*ptr); | |||
680 | if (res) Logger_Abort2(res, "Detaching thread"); | |||
681 | Mem_Free(ptr); | |||
682 | } | |||
683 | ||||
684 | void Thread_Join(void* handle) { | |||
685 | pthread_t* ptr = (pthread_t*)handle; | |||
686 | int res = pthread_join(*ptr, NULL((void*)0)); | |||
687 | if (res) Logger_Abort2(res, "Joining thread"); | |||
688 | Mem_Free(ptr); | |||
689 | } | |||
690 | ||||
691 | void* Mutex_Create(void) { | |||
692 | pthread_mutex_t* ptr = (pthread_mutex_t*)Mem_Alloc(1, sizeof(pthread_mutex_t), "mutex"); | |||
693 | int res = pthread_mutex_init(ptr, NULL((void*)0)); | |||
694 | if (res) Logger_Abort2(res, "Creating mutex"); | |||
695 | return ptr; | |||
696 | } | |||
697 | ||||
698 | void Mutex_Free(void* handle) { | |||
699 | int res = pthread_mutex_destroy((pthread_mutex_t*)handle); | |||
700 | if (res) Logger_Abort2(res, "Destroying mutex"); | |||
701 | Mem_Free(handle); | |||
702 | } | |||
703 | ||||
704 | void Mutex_Lock(void* handle) { | |||
705 | int res = pthread_mutex_lock((pthread_mutex_t*)handle); | |||
706 | if (res) Logger_Abort2(res, "Locking mutex"); | |||
707 | } | |||
708 | ||||
709 | void Mutex_Unlock(void* handle) { | |||
710 | int res = pthread_mutex_unlock((pthread_mutex_t*)handle); | |||
711 | if (res) Logger_Abort2(res, "Unlocking mutex"); | |||
712 | } | |||
713 | ||||
714 | struct WaitData { | |||
715 | pthread_cond_t cond; | |||
716 | pthread_mutex_t mutex; | |||
717 | int signalled; | |||
718 | }; | |||
719 | ||||
720 | void* Waitable_Create(void) { | |||
721 | struct WaitData* ptr = (struct WaitData*)Mem_Alloc(1, sizeof(struct WaitData), "waitable"); | |||
722 | int res; | |||
723 | ||||
724 | res = pthread_cond_init(&ptr->cond, NULL((void*)0)); | |||
725 | if (res) Logger_Abort2(res, "Creating waitable"); | |||
726 | res = pthread_mutex_init(&ptr->mutex, NULL((void*)0)); | |||
727 | if (res) Logger_Abort2(res, "Creating waitable mutex"); | |||
728 | ||||
729 | ptr->signalled = false0; | |||
730 | return ptr; | |||
731 | } | |||
732 | ||||
733 | void Waitable_Free(void* handle) { | |||
734 | struct WaitData* ptr = (struct WaitData*)handle; | |||
735 | int res; | |||
736 | ||||
737 | res = pthread_cond_destroy(&ptr->cond); | |||
738 | if (res) Logger_Abort2(res, "Destroying waitable"); | |||
739 | res = pthread_mutex_destroy(&ptr->mutex); | |||
740 | if (res) Logger_Abort2(res, "Destroying waitable mutex"); | |||
741 | Mem_Free(handle); | |||
742 | } | |||
743 | ||||
744 | void Waitable_Signal(void* handle) { | |||
745 | struct WaitData* ptr = (struct WaitData*)handle; | |||
746 | int res; | |||
747 | ||||
748 | Mutex_Lock(&ptr->mutex); | |||
749 | ptr->signalled = true1; | |||
750 | Mutex_Unlock(&ptr->mutex); | |||
751 | ||||
752 | res = pthread_cond_signal(&ptr->cond); | |||
753 | if (res) Logger_Abort2(res, "Signalling event"); | |||
754 | } | |||
755 | ||||
756 | void Waitable_Wait(void* handle) { | |||
757 | struct WaitData* ptr = (struct WaitData*)handle; | |||
758 | int res; | |||
759 | ||||
760 | Mutex_Lock(&ptr->mutex); | |||
761 | if (!ptr->signalled) { | |||
762 | res = pthread_cond_wait(&ptr->cond, &ptr->mutex); | |||
763 | if (res) Logger_Abort2(res, "Waitable wait"); | |||
764 | } | |||
765 | ptr->signalled = false0; | |||
766 | Mutex_Unlock(&ptr->mutex); | |||
767 | } | |||
768 | ||||
769 | void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { | |||
770 | struct WaitData* ptr = (struct WaitData*)handle; | |||
771 | struct timeval tv; | |||
772 | struct timespec ts; | |||
773 | int res; | |||
774 | gettimeofday(&tv, NULL((void*)0)); | |||
775 | ||||
776 | /* absolute time for some silly reason */ | |||
777 | ts.tv_sec = tv.tv_sec + milliseconds / 1000; | |||
778 | ts.tv_nsec = 1000 * (tv.tv_usec + 1000 * (milliseconds % 1000)); | |||
779 | ts.tv_sec += ts.tv_nsec / NS_PER_SEC1000000000ULL; | |||
780 | ts.tv_nsec %= NS_PER_SEC1000000000ULL; | |||
781 | ||||
782 | Mutex_Lock(&ptr->mutex); | |||
783 | if (!ptr->signalled) { | |||
784 | res = pthread_cond_timedwait(&ptr->cond, &ptr->mutex, &ts); | |||
785 | if (res && res != ETIMEDOUT60) Logger_Abort2(res, "Waitable wait for"); | |||
786 | } | |||
787 | ptr->signalled = false0; | |||
788 | Mutex_Unlock(&ptr->mutex); | |||
789 | } | |||
790 | #endif | |||
791 | ||||
792 | ||||
793 | /*########################################################################################################################* | |||
794 | *--------------------------------------------------------Font/Text--------------------------------------------------------* | |||
795 | *#########################################################################################################################*/ | |||
796 | #ifdef CC_BUILD_WEB | |||
797 | void Platform_LoadSysFonts(void) { } | |||
798 | #else | |||
799 | static void FontDirCallback(const cc_string* path, void* obj) { | |||
800 | static const cc_string fonExt = String_FromConst(".fon"){ ".fon", (sizeof(".fon") - 1), (sizeof(".fon") - 1)}; | |||
801 | /* Completely skip windows .FON files */ | |||
802 | if (String_CaselessEnds(path, &fonExt)) return; | |||
803 | SysFonts_Register(path); | |||
804 | } | |||
805 | ||||
806 | void Platform_LoadSysFonts(void) { | |||
807 | int i; | |||
808 | #if defined CC_BUILD_WIN | |||
809 | char winFolder[FILENAME_SIZE260]; | |||
810 | TCHAR winTmp[FILENAME_SIZE260]; | |||
811 | UINT winLen; | |||
812 | /* System folder path may not be C:/Windows */ | |||
813 | cc_string dirs[1]; | |||
814 | String_InitArray(dirs[0], winFolder)dirs[0].buffer = winFolder; dirs[0].length = 0; dirs[0].capacity = sizeof(winFolder);; | |||
815 | ||||
816 | winLen = GetWindowsDirectory(winTmp, FILENAME_SIZE260); | |||
817 | if (winLen) { | |||
818 | Platform_DecodeString(&dirs[0], winTmp, winLen); | |||
819 | } else { | |||
820 | String_AppendConst(&dirs[0], "C:/Windows"); | |||
821 | } | |||
822 | String_AppendConst(&dirs[0], "/fonts"); | |||
823 | ||||
824 | #elif defined CC_BUILD_ANDROID | |||
825 | static const cc_string dirs[3] = { | |||
826 | String_FromConst("/system/fonts"){ "/system/fonts", (sizeof("/system/fonts") - 1), (sizeof("/system/fonts" ) - 1)}, | |||
827 | String_FromConst("/system/font"){ "/system/font", (sizeof("/system/font") - 1), (sizeof("/system/font" ) - 1)}, | |||
828 | String_FromConst("/data/fonts"){ "/data/fonts", (sizeof("/data/fonts") - 1), (sizeof("/data/fonts" ) - 1)}, | |||
829 | }; | |||
830 | #elif defined CC_BUILD_NETBSD | |||
831 | static const cc_string dirs[3] = { | |||
832 | String_FromConst("/usr/X11R7/lib/X11/fonts"){ "/usr/X11R7/lib/X11/fonts", (sizeof("/usr/X11R7/lib/X11/fonts" ) - 1), (sizeof("/usr/X11R7/lib/X11/fonts") - 1)}, | |||
833 | String_FromConst("/usr/pkg/lib/X11/fonts"){ "/usr/pkg/lib/X11/fonts", (sizeof("/usr/pkg/lib/X11/fonts") - 1), (sizeof("/usr/pkg/lib/X11/fonts") - 1)}, | |||
834 | String_FromConst("/usr/pkg/share/fonts"){ "/usr/pkg/share/fonts", (sizeof("/usr/pkg/share/fonts") - 1 ), (sizeof("/usr/pkg/share/fonts") - 1)} | |||
835 | }; | |||
836 | #elif defined CC_BUILD_HAIKU | |||
837 | static const cc_string dirs[1] = { | |||
838 | String_FromConst("/system/data/fonts"){ "/system/data/fonts", (sizeof("/system/data/fonts") - 1), ( sizeof("/system/data/fonts") - 1)} | |||
839 | }; | |||
840 | #elif defined CC_BUILD_OSX | |||
841 | static const cc_string dirs[2] = { | |||
842 | String_FromConst("/System/Library/Fonts"){ "/System/Library/Fonts", (sizeof("/System/Library/Fonts") - 1), (sizeof("/System/Library/Fonts") - 1)}, | |||
843 | String_FromConst("/Library/Fonts"){ "/Library/Fonts", (sizeof("/Library/Fonts") - 1), (sizeof("/Library/Fonts" ) - 1)} | |||
844 | }; | |||
845 | #elif defined CC_BUILD_POSIX | |||
846 | static const cc_string dirs[2] = { | |||
847 | String_FromConst("/usr/share/fonts"){ "/usr/share/fonts", (sizeof("/usr/share/fonts") - 1), (sizeof ("/usr/share/fonts") - 1)}, | |||
848 | String_FromConst("/usr/local/share/fonts"){ "/usr/local/share/fonts", (sizeof("/usr/local/share/fonts") - 1), (sizeof("/usr/local/share/fonts") - 1)} | |||
849 | }; | |||
850 | #endif | |||
851 | for (i = 0; i < Array_Elems(dirs)(sizeof(dirs) / sizeof(dirs[0])); i++) { | |||
852 | Directory_Enum(&dirs[i], NULL((void*)0), FontDirCallback); | |||
853 | } | |||
854 | } | |||
855 | #endif | |||
856 | ||||
857 | ||||
858 | /*########################################################################################################################* | |||
859 | *---------------------------------------------------------Socket----------------------------------------------------------* | |||
860 | *#########################################################################################################################*/ | |||
861 | cc_result Socket_Create(cc_socket* s) { | |||
862 | *s = socket(AF_INET2, SOCK_STREAM1, IPPROTO_TCP6); | |||
863 | return *s == -1 ? Socket__Error()(*__errno()) : 0; | |||
864 | } | |||
865 | ||||
866 | static cc_result Socket_ioctl(cc_socket s, cc_uint32 cmd, int* data) { | |||
867 | #if defined CC_BUILD_WIN | |||
868 | return ioctlsocket(s, cmd, data); | |||
869 | #else | |||
870 | return ioctl(s, cmd, data); | |||
871 | #endif | |||
872 | } | |||
873 | ||||
874 | cc_result Socket_Available(cc_socket s, cc_uint32* available) { | |||
875 | return Socket_ioctl(s, FIONREAD((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((127))), available); | |||
876 | } | |||
877 | cc_result Socket_SetBlocking(cc_socket s, cc_bool blocking) { | |||
878 | #if defined CC_BUILD_WEB | |||
879 | return ERR_NOT_SUPPORTED; /* sockets always async */ | |||
880 | #else | |||
881 | int blocking_raw = blocking ? 0 : -1; | |||
882 | return Socket_ioctl(s, FIONBIO((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((126))), &blocking_raw); | |||
883 | #endif | |||
884 | } | |||
885 | ||||
886 | cc_result Socket_GetError(cc_socket s, cc_result* result) { | |||
887 | socklen_t resultSize = sizeof(cc_result); | |||
888 | return getsockopt(s, SOL_SOCKET0xffff, SO_ERROR0x1007, result, &resultSize); | |||
889 | } | |||
890 | ||||
891 | cc_result Socket_Connect(cc_socket s, const cc_string* ip, int port) { | |||
892 | struct sockaddr addr; | |||
893 | cc_result res; | |||
894 | ||||
895 | addr.sa_family = AF_INET2; | |||
896 | Stream_SetU16_BE( (cc_uint8*)&addr.sa_data[0], port); | |||
897 | Utils_ParseIP(ip, (cc_uint8*)&addr.sa_data[2]); | |||
898 | ||||
899 | res = connect(s, &addr, sizeof(addr)); | |||
900 | return res == -1 ? Socket__Error()(*__errno()) : 0; | |||
901 | } | |||
902 | ||||
903 | cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { | |||
904 | #ifdef CC_BUILD_WEB | |||
905 | /* recv only reads one WebSocket frame at most, hence call it multiple times */ | |||
906 | int recvCount = 0, pending; | |||
907 | *modified = 0; | |||
908 | ||||
909 | while (count && !Socket_Available(s, &pending) && pending) { | |||
910 | recvCount = recv(s, data, count, 0); | |||
911 | if (recvCount == -1) return Socket__Error()(*__errno()); | |||
912 | ||||
913 | *modified += recvCount; | |||
914 | data += recvCount; count -= recvCount; | |||
915 | } | |||
916 | return 0; | |||
917 | #else | |||
918 | int recvCount = recv(s, data, count, 0); | |||
919 | if (recvCount != -1) { *modified = recvCount; return 0; } | |||
920 | *modified = 0; return Socket__Error()(*__errno()); | |||
921 | #endif | |||
922 | } | |||
923 | ||||
924 | cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { | |||
925 | int sentCount = send(s, data, count, 0); | |||
926 | if (sentCount != -1) { *modified = sentCount; return 0; } | |||
927 | *modified = 0; return Socket__Error()(*__errno()); | |||
928 | } | |||
929 | ||||
930 | cc_result Socket_Close(cc_socket s) { | |||
931 | cc_result res = 0; | |||
932 | cc_result res1, res2; | |||
933 | ||||
934 | #if defined CC_BUILD_WEB | |||
935 | res1 = 0; | |||
936 | #elif defined CC_BUILD_WIN | |||
937 | res1 = shutdown(s, SD_BOTH); | |||
938 | #else | |||
939 | res1 = shutdown(s, SHUT_RDWR2); | |||
940 | #endif | |||
941 | if (res1 == -1) res = Socket__Error()(*__errno()); | |||
942 | ||||
943 | #if defined CC_BUILD_WIN | |||
944 | res2 = closesocket(s); | |||
945 | #else | |||
946 | res2 = close(s); | |||
947 | #endif | |||
948 | if (res2 == -1) res = Socket__Error()(*__errno()); | |||
949 | return res; | |||
950 | } | |||
951 | ||||
952 | /* Alas, a simple cross-platform select() is not good enough */ | |||
953 | #if defined CC_BUILD_WIN | |||
954 | cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { | |||
955 | fd_set set; | |||
956 | struct timeval time = { 0 }; | |||
957 | int selectCount; | |||
958 | ||||
959 | set.fd_count = 1; | |||
960 | set.fd_array[0] = s; | |||
961 | ||||
962 | if (mode == SOCKET_POLL_READ) { | |||
963 | selectCount = select(1, &set, NULL((void*)0), NULL((void*)0), &time); | |||
964 | } else { | |||
965 | selectCount = select(1, NULL((void*)0), &set, NULL((void*)0), &time); | |||
966 | } | |||
967 | ||||
968 | if (selectCount == -1) { *success = false0; return Socket__Error()(*__errno()); } | |||
969 | ||||
970 | *success = set.fd_count != 0; return 0; | |||
971 | } | |||
972 | #elif defined CC_BUILD_OSX | |||
973 | /* poll is broken on old OSX apparently https://daniel.haxx.se/docs/poll-vs-select.html */ | |||
974 | cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { | |||
975 | fd_set set; | |||
976 | struct timeval time = { 0 }; | |||
977 | int selectCount; | |||
978 | ||||
979 | FD_ZERO(&set)do { fd_set *_p = (&set); __size_t _n = (((1024) + ((((unsigned )(sizeof(__fd_mask) * 8))) - 1)) / (((unsigned)(sizeof(__fd_mask ) * 8)))); while (_n > 0) _p->fds_bits[--_n] = 0; } while (0); | |||
980 | FD_SET(s, &set)__fd_set((s), (&set)); | |||
981 | ||||
982 | if (mode == SOCKET_POLL_READ) { | |||
983 | selectCount = select(s + 1, &set, NULL((void*)0), NULL((void*)0), &time); | |||
984 | } else { | |||
985 | selectCount = select(s + 1, NULL((void*)0), &set, NULL((void*)0), &time); | |||
986 | } | |||
987 | ||||
988 | if (selectCount == -1) { *success = false0; return Socket__Error()(*__errno()); } | |||
989 | *success = FD_ISSET(s, &set)__fd_isset((s), (&set)) != 0; return 0; | |||
990 | } | |||
991 | #else | |||
992 | #include <poll.h> | |||
993 | cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { | |||
994 | struct pollfd pfd; | |||
995 | int flags; | |||
996 | ||||
997 | pfd.fd = s; | |||
998 | pfd.events = mode == SOCKET_POLL_READ ? POLLIN0x0001 : POLLOUT0x0004; | |||
999 | if (poll(&pfd, 1, 0) == -1) { *success = false0; return Socket__Error()(*__errno()); } | |||
1000 | ||||
1001 | /* to match select, closed socket still counts as readable */ | |||
1002 | flags = mode == SOCKET_POLL_READ ? (POLLIN0x0001 | POLLHUP0x0010) : POLLOUT0x0004; | |||
1003 | *success = (pfd.revents & flags) != 0; | |||
1004 | return 0; | |||
1005 | } | |||
1006 | #endif | |||
1007 | ||||
1008 | ||||
1009 | /*########################################################################################################################* | |||
1010 | *-----------------------------------------------------Process/Module------------------------------------------------------* | |||
1011 | *#########################################################################################################################*/ | |||
1012 | #if defined CC_BUILD_WIN | |||
1013 | static cc_result Process_RawStart(const TCHAR* path, TCHAR* args) { | |||
1014 | STARTUPINFO si = { 0 }; | |||
1015 | PROCESS_INFORMATION pi = { 0 }; | |||
1016 | BOOL ok; | |||
1017 | ||||
1018 | si.cb = sizeof(STARTUPINFO); | |||
1019 | ok = CreateProcess(path, args, NULL((void*)0), NULL((void*)0), false0, 0, NULL((void*)0), NULL((void*)0), &si, &pi); | |||
1020 | if (!ok) return GetLastError(); | |||
1021 | ||||
1022 | /* Don't leak memory for proess return code */ | |||
1023 | CloseHandle(pi.hProcess); | |||
1024 | CloseHandle(pi.hThread); | |||
1025 | return 0; | |||
1026 | } | |||
1027 | ||||
1028 | static cc_result Process_RawGetExePath(TCHAR* path, int* len) { | |||
1029 | *len = GetModuleFileName(NULL((void*)0), path, NATIVE_STR_LEN600); | |||
1030 | return *len ? 0 : GetLastError(); | |||
1031 | } | |||
1032 | ||||
1033 | cc_result Process_StartGame(const cc_string* args) { | |||
1034 | cc_string argv; char argvBuffer[NATIVE_STR_LEN600]; | |||
1035 | TCHAR raw[NATIVE_STR_LEN600], path[NATIVE_STR_LEN600 + 1]; | |||
1036 | int len; | |||
1037 | ||||
1038 | cc_result res = Process_RawGetExePath(path, &len); | |||
1039 | if (res) return res; | |||
1040 | path[len] = '\0'; | |||
1041 | ||||
1042 | String_InitArray(argv, argvBuffer)argv.buffer = argvBuffer; argv.length = 0; argv.capacity = sizeof (argvBuffer);; | |||
1043 | String_Format1(&argv, "ClassiCube.exe %s", args); | |||
1044 | Platform_EncodeString(raw, &argv); | |||
1045 | return Process_RawStart(path, raw); | |||
1046 | } | |||
1047 | void Process_Exit(cc_result code) { ExitProcess(code); } | |||
1048 | ||||
1049 | void Process_StartOpen(const cc_string* args) { | |||
1050 | TCHAR str[NATIVE_STR_LEN600]; | |||
1051 | Platform_EncodeString(str, args); | |||
1052 | ShellExecute(NULL((void*)0), NULL((void*)0), str, NULL((void*)0), NULL((void*)0), SW_SHOWNORMAL); | |||
1053 | } | |||
1054 | #elif defined CC_BUILD_WEB | |||
1055 | cc_result Process_StartGame(const cc_string* args) { return ERR_NOT_SUPPORTED; } | |||
1056 | void Process_Exit(cc_result code) { exit(code); } | |||
1057 | ||||
1058 | void Process_StartOpen(const cc_string* args) { | |||
1059 | char str[NATIVE_STR_LEN600]; | |||
1060 | Platform_EncodeString(str, args); | |||
1061 | EM_ASM_({ window.open(UTF8ToString($0)); }, str); | |||
1062 | } | |||
1063 | #elif defined CC_BUILD_ANDROID | |||
1064 | static char gameArgsBuffer[512]; | |||
1065 | static cc_string gameArgs = String_FromArray(gameArgsBuffer){ gameArgsBuffer, 0, sizeof(gameArgsBuffer)}; | |||
1066 | ||||
1067 | cc_result Process_StartGame(const cc_string* args) { | |||
1068 | String_Copy(&gameArgs, args); | |||
1069 | return 0; /* TODO: Is there a clean way of handling an error */ | |||
1070 | } | |||
1071 | void Process_Exit(cc_result code) { exit(code); } | |||
1072 | ||||
1073 | void Process_StartOpen(const cc_string* args) { | |||
1074 | JavaCall_String_Void("startOpen", args); | |||
1075 | } | |||
1076 | #elif defined CC_BUILD_POSIX | |||
1077 | static cc_result Process_RawStart(const char* path, char** argv) { | |||
1078 | pid_t pid = fork(); | |||
1079 | if (pid == -1) return errno(*__errno()); | |||
1080 | ||||
1081 | if (pid == 0) { | |||
1082 | /* Executed in child process */ | |||
1083 | execvp(path, argv); | |||
1084 | _exit(127); /* "command not found" */ | |||
1085 | } else { | |||
1086 | /* Executed in parent process */ | |||
1087 | /* We do nothing here.. */ | |||
1088 | return 0; | |||
1089 | } | |||
1090 | } | |||
1091 | ||||
1092 | static cc_result Process_RawGetExePath(char* path, int* len); | |||
1093 | ||||
1094 | cc_result Process_StartGame(const cc_string* args) { | |||
1095 | char path[NATIVE_STR_LEN600], raw[NATIVE_STR_LEN600]; | |||
1096 | int i, j, len = 0; | |||
1097 | char* argv[15]; | |||
1098 | ||||
1099 | cc_result res = Process_RawGetExePath(path, &len); | |||
1100 | if (res) return res; | |||
1101 | path[len] = '\0'; | |||
1102 | ||||
1103 | Platform_EncodeString(raw, args); | |||
1104 | argv[0] = path; argv[1] = raw; | |||
1105 | ||||
1106 | /* need to null-terminate multiple arguments */ | |||
1107 | for (i = 0, j = 2; raw[i] && i < Array_Elems(raw)(sizeof(raw) / sizeof(raw[0])); i++) { | |||
1108 | if (raw[i] != ' ') continue; | |||
1109 | ||||
1110 | /* null terminate previous argument */ | |||
1111 | raw[i] = '\0'; | |||
1112 | argv[j++] = &raw[i + 1]; | |||
1113 | } | |||
1114 | ||||
1115 | if (defaultDirectory) { argv[j++] = defaultDirectory; } | |||
1116 | argv[j] = NULL((void*)0); | |||
1117 | return Process_RawStart(path, argv); | |||
1118 | } | |||
1119 | ||||
1120 | void Process_Exit(cc_result code) { exit(code); } | |||
1121 | ||||
1122 | /* Opening browser/starting shell is not really standardised */ | |||
1123 | #if defined CC_BUILD_OSX | |||
1124 | void Process_StartOpen(const cc_string* args) { | |||
1125 | UInt8 str[NATIVE_STR_LEN600]; | |||
1126 | CFURLRef urlCF; | |||
1127 | int len; | |||
1128 | ||||
1129 | len = Platform_EncodeString(str, args); | |||
1130 | urlCF = CFURLCreateWithBytes(kCFAllocatorDefault, str, len, kCFStringEncodingUTF8, NULL((void*)0)); | |||
1131 | LSOpenCFURLRef(urlCF, NULL((void*)0)); | |||
1132 | CFRelease(urlCF); | |||
1133 | } | |||
1134 | #elif defined CC_BUILD_HAIKU | |||
1135 | void Process_StartOpen(const cc_string* args) { | |||
1136 | char str[NATIVE_STR_LEN600]; | |||
1137 | char* cmd[3]; | |||
1138 | Platform_EncodeString(str, args); | |||
1139 | ||||
1140 | cmd[0] = "open"; cmd[1] = str; cmd[2] = NULL((void*)0); | |||
1141 | Process_RawStart("open", cmd); | |||
1142 | } | |||
1143 | #else | |||
1144 | void Process_StartOpen(const cc_string* args) { | |||
1145 | char str[NATIVE_STR_LEN600]; | |||
1146 | char* cmd[3]; | |||
1147 | Platform_EncodeString(str, args); | |||
1148 | ||||
1149 | /* TODO: Can xdg-open be used on original Solaris, or is it just an OpenIndiana thing */ | |||
1150 | cmd[0] = "xdg-open"; cmd[1] = str; cmd[2] = NULL((void*)0); | |||
1151 | Process_RawStart("xdg-open", cmd); | |||
1152 | } | |||
1153 | #endif | |||
1154 | ||||
1155 | /* Retrieving exe path is completely OS dependant */ | |||
1156 | #if defined CC_BUILD_OSX | |||
1157 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1158 | Mem_Set(path, '\0', NATIVE_STR_LEN600); | |||
1159 | cc_uint32 size = NATIVE_STR_LEN600; | |||
1160 | if (_NSGetExecutablePath(path, &size)) return ERR_INVALID_ARGUMENT; | |||
1161 | ||||
1162 | /* despite what you'd assume, size is NOT changed to length of path */ | |||
1163 | *len = String_CalcLen(path, NATIVE_STR_LEN600); | |||
1164 | return 0; | |||
1165 | } | |||
1166 | #elif defined CC_BUILD_LINUX | |||
1167 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1168 | *len = readlink("/proc/self/exe", path, NATIVE_STR_LEN600); | |||
1169 | return *len == -1 ? errno(*__errno()) : 0; | |||
1170 | } | |||
1171 | #elif defined CC_BUILD_FREEBSD | |||
1172 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1173 | static int mib[4] = { CTL_KERN1, KERN_PROC66, KERN_PROC_PATHNAME, -1 }; | |||
1174 | size_t size = NATIVE_STR_LEN600; | |||
1175 | ||||
1176 | if (sysctl(mib, 4, path, &size, NULL((void*)0), 0) == -1) return errno(*__errno()); | |||
1177 | *len = String_CalcLen(path, NATIVE_STR_LEN600); | |||
1178 | return 0; | |||
1179 | } | |||
1180 | #elif defined CC_BUILD_OPENBSD | |||
1181 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1182 | static int mib[4] = { CTL_KERN1, KERN_PROC_ARGS55, 0, KERN_PROC_ARGV1 }; | |||
1183 | char tmp[NATIVE_STR_LEN600]; | |||
1184 | size_t size; | |||
1185 | char* argv[100]; | |||
1186 | char* str; | |||
1187 | ||||
1188 | /* NOTE: OpenBSD doesn't seem to let us get executable's location, so fallback to argv[0] */ | |||
1189 | /* See OpenBSD sysctl manpage for why argv array is so large */ | |||
1190 | /*... The buffer pointed to by oldp is filled with an array of char pointers followed by the strings themselves... */ | |||
1191 | mib[2] = getpid(); | |||
1192 | size = 100 * sizeof(char*); | |||
1193 | if (sysctl(mib, 4, argv, &size, NULL((void*)0), 0) == -1) return errno(*__errno()); | |||
1194 | ||||
1195 | str = argv[0]; | |||
1196 | if (str[0] != '/') { | |||
1197 | /* relative path */ | |||
1198 | if (!realpath(str, tmp)) return errno(*__errno()); | |||
1199 | str = tmp; | |||
1200 | } | |||
1201 | ||||
1202 | *len = String_CalcLen(str, NATIVE_STR_LEN600); | |||
1203 | Mem_Copy(path, str, *len); | |||
1204 | return 0; | |||
1205 | } | |||
1206 | #elif defined CC_BUILD_NETBSD | |||
1207 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1208 | static int mib[4] = { CTL_KERN1, KERN_PROC_ARGS55, -1, KERN_PROC_PATHNAME }; | |||
1209 | size_t size = NATIVE_STR_LEN600; | |||
1210 | ||||
1211 | if (sysctl(mib, 4, path, &size, NULL((void*)0), 0) == -1) return errno(*__errno()); | |||
1212 | *len = String_CalcLen(path, NATIVE_STR_LEN600); | |||
1213 | return 0; | |||
1214 | } | |||
1215 | #elif defined CC_BUILD_SOLARIS | |||
1216 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1217 | *len = readlink("/proc/self/path/a.out", path, NATIVE_STR_LEN600); | |||
1218 | return *len == -1 ? errno(*__errno()) : 0; | |||
1219 | } | |||
1220 | #elif defined CC_BUILD_HAIKU | |||
1221 | static cc_result Process_RawGetExePath(char* path, int* len) { | |||
1222 | image_info info; | |||
1223 | int32 cookie = 0; | |||
1224 | ||||
1225 | cc_result res = get_next_image_info(B_CURRENT_TEAM, &cookie, &info); | |||
1226 | if (res != B_OK) return res; | |||
1227 | ||||
1228 | *len = String_CalcLen(info.name, NATIVE_STR_LEN600); | |||
1229 | Mem_Copy(path, info.name, *len); | |||
1230 | return 0; | |||
1231 | } | |||
1232 | #endif | |||
1233 | #endif /* CC_BUILD_POSIX */ | |||
1234 | ||||
1235 | ||||
1236 | /*########################################################################################################################* | |||
1237 | *--------------------------------------------------------Updater----------------------------------------------------------* | |||
1238 | *#########################################################################################################################*/ | |||
1239 | #if defined CC_BUILD_WIN | |||
1240 | #define UPDATE_TMP TEXT("CC_prev.exe") | |||
1241 | #define UPDATE_SRC TEXT(UPDATE_FILE"ClassiCube.update") | |||
1242 | ||||
1243 | #if _WIN64 | |||
1244 | const char* const Updater_D3D9 = "ClassiCube.64.exe"; | |||
1245 | const char* const Updater_OGL = "ClassiCube.64-opengl.exe"; | |||
1246 | #else | |||
1247 | const char* const Updater_D3D9 = "ClassiCube.exe"; | |||
1248 | const char* const Updater_OGL = "ClassiCube.opengl.exe"; | |||
1249 | #endif | |||
1250 | ||||
1251 | cc_bool Updater_Clean(void) { | |||
1252 | return DeleteFile(UPDATE_TMP) || GetLastError() == ERROR_FILE_NOT_FOUND; | |||
1253 | } | |||
1254 | ||||
1255 | cc_result Updater_Start(const char** action) { | |||
1256 | TCHAR path[NATIVE_STR_LEN600 + 1]; | |||
1257 | TCHAR args[2] = { 'a', '\0' }; /* don't actually care about arguments */ | |||
1258 | cc_result res; | |||
1259 | int len = 0; | |||
1260 | ||||
1261 | *action = "Getting executable path"; | |||
1262 | if ((res = Process_RawGetExePath(path, &len))) return res; | |||
1263 | path[len] = '\0'; | |||
1264 | ||||
1265 | *action = "Moving executable to CC_prev.exe"; | |||
1266 | if (!MoveFileEx(path, UPDATE_TMP, MOVEFILE_REPLACE_EXISTING)) return GetLastError(); | |||
1267 | *action = "Replacing executable"; | |||
1268 | if (!MoveFileEx(UPDATE_SRC, path, MOVEFILE_REPLACE_EXISTING)) return GetLastError(); | |||
1269 | ||||
1270 | *action = "Restarting game"; | |||
1271 | return Process_RawStart(path, args); | |||
1272 | } | |||
1273 | ||||
1274 | cc_result Updater_GetBuildTime(cc_uint64* timestamp) { | |||
1275 | TCHAR path[NATIVE_STR_LEN600 + 1]; | |||
1276 | cc_file file; | |||
1277 | FILETIME ft; | |||
1278 | cc_uint64 raw; | |||
1279 | int len = 0; | |||
1280 | ||||
1281 | cc_result res = Process_RawGetExePath(path, &len); | |||
1282 | if (res) return res; | |||
1283 | path[len] = '\0'; | |||
1284 | ||||
1285 | file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL((void*)0), OPEN_EXISTING, 0, NULL((void*)0)); | |||
1286 | if (file == INVALID_HANDLE_VALUE) return GetLastError(); | |||
1287 | ||||
1288 | if (GetFileTime(file, NULL((void*)0), NULL((void*)0), &ft)) { | |||
1289 | raw = ft.dwLowDateTime | ((cc_uint64)ft.dwHighDateTime << 32); | |||
1290 | *timestamp = FileTime_UnixTime(raw); | |||
1291 | } else { | |||
1292 | res = GetLastError(); | |||
1293 | } | |||
1294 | ||||
1295 | File_Close(file); | |||
1296 | return res; | |||
1297 | } | |||
1298 | ||||
1299 | /* Don't need special execute permission on windows */ | |||
1300 | cc_result Updater_MarkExecutable(void) { return 0; } | |||
1301 | cc_result Updater_SetNewBuildTime(cc_uint64 timestamp) { | |||
1302 | static const cc_string path = String_FromConst(UPDATE_FILE){ "ClassiCube.update", (sizeof("ClassiCube.update") - 1), (sizeof ("ClassiCube.update") - 1)}; | |||
1303 | cc_file file; | |||
1304 | FILETIME ft; | |||
1305 | cc_uint64 raw; | |||
1306 | cc_result res = File_OpenOrCreate(&file, &path); | |||
1307 | if (res) return res; | |||
1308 | ||||
1309 | raw = 10000000 * (timestamp + FILETIME_UNIX_EPOCH); | |||
1310 | ft.dwLowDateTime = (cc_uint32)raw; | |||
1311 | ft.dwHighDateTime = (cc_uint32)(raw >> 32); | |||
1312 | ||||
1313 | if (!SetFileTime(file, NULL((void*)0), NULL((void*)0), &ft)) res = GetLastError(); | |||
1314 | File_Close(file); | |||
1315 | return res; | |||
1316 | } | |||
1317 | ||||
1318 | #elif defined CC_BUILD_WEB || defined CC_BUILD_ANDROID | |||
1319 | const char* const Updater_D3D9 = NULL((void*)0); | |||
1320 | const char* const Updater_OGL = NULL((void*)0); | |||
1321 | ||||
1322 | #if defined CC_BUILD_WEB | |||
1323 | cc_result Updater_GetBuildTime(cc_uint64* t) { return ERR_NOT_SUPPORTED; } | |||
1324 | #else | |||
1325 | cc_result Updater_GetBuildTime(cc_uint64* t) { | |||
1326 | JNIEnv* env; | |||
1327 | JavaGetCurrentEnv(env); | |||
1328 | ||||
1329 | /* https://developer.android.com/reference/java/io/File#lastModified() */ | |||
1330 | /* lastModified is returned in milliseconds */ | |||
1331 | *t = JavaCallLong(env, "getApkUpdateTime", "()J", NULL((void*)0)) / 1000; | |||
1332 | return 0; | |||
1333 | } | |||
1334 | #endif | |||
1335 | ||||
1336 | cc_bool Updater_Clean(void) { return true1; } | |||
1337 | cc_result Updater_Start(const char** action) { *action = "Updating game"; return ERR_NOT_SUPPORTED; } | |||
1338 | cc_result Updater_MarkExecutable(void) { return 0; } | |||
1339 | cc_result Updater_SetNewBuildTime(cc_uint64 t) { return ERR_NOT_SUPPORTED; } | |||
1340 | #elif defined CC_BUILD_POSIX | |||
1341 | cc_bool Updater_Clean(void) { return true1; } | |||
1342 | ||||
1343 | const char* const Updater_D3D9 = NULL((void*)0); | |||
1344 | #if defined CC_BUILD_LINUX | |||
1345 | #if __x86_64__1 | |||
1346 | const char* const Updater_OGL = "ClassiCube"; | |||
1347 | #elif __i386__ | |||
1348 | const char* const Updater_OGL = "ClassiCube.32"; | |||
1349 | #elif CC_BUILD_RPI | |||
1350 | const char* const Updater_OGL = "ClassiCube.rpi"; | |||
1351 | #else | |||
1352 | const char* const Updater_OGL = NULL((void*)0); | |||
1353 | #endif | |||
1354 | #elif defined CC_BUILD_OSX | |||
1355 | #if __x86_64__1 | |||
1356 | const char* const Updater_OGL = "ClassiCube.64.osx"; | |||
1357 | #elif __i386__ | |||
1358 | const char* const Updater_OGL = "ClassiCube.osx"; | |||
1359 | #else | |||
1360 | const char* const Updater_OGL = NULL((void*)0); | |||
1361 | #endif | |||
1362 | #else | |||
1363 | const char* const Updater_OGL = NULL((void*)0); | |||
1364 | #endif | |||
1365 | ||||
1366 | cc_result Updater_Start(const char** action) { | |||
1367 | char path[NATIVE_STR_LEN600 + 1]; | |||
1368 | char* argv[2]; | |||
1369 | cc_result res; | |||
1370 | int len = 0; | |||
1371 | ||||
1372 | *action = "Getting executable path"; | |||
1373 | if ((res = Process_RawGetExePath(path, &len))) return res; | |||
1374 | path[len] = '\0'; | |||
1375 | ||||
1376 | /* Because the process is only referenced by inode, we can */ | |||
1377 | /* just unlink current filename and rename updated file to it */ | |||
1378 | *action = "Deleting executable"; | |||
1379 | if (unlink(path) == -1) return errno(*__errno()); | |||
1380 | *action = "Replacing executable"; | |||
1381 | if (rename(UPDATE_FILE"ClassiCube.update", path) == -1) return errno(*__errno()); | |||
1382 | ||||
1383 | argv[0] = path; argv[1] = NULL((void*)0); | |||
1384 | *action = "Restarting game"; | |||
1385 | return Process_RawStart(path, argv); | |||
1386 | } | |||
1387 | ||||
1388 | cc_result Updater_GetBuildTime(cc_uint64* timestamp) { | |||
1389 | char path[NATIVE_STR_LEN600 + 1]; | |||
1390 | struct stat sb; | |||
1391 | int len = 0; | |||
1392 | ||||
1393 | cc_result res = Process_RawGetExePath(path, &len); | |||
1394 | if (res) return res; | |||
1395 | path[len] = '\0'; | |||
1396 | ||||
1397 | if (stat(path, &sb) == -1) return errno(*__errno()); | |||
1398 | *timestamp = (cc_uint64)sb.st_mtimest_mtim.tv_sec; | |||
1399 | return 0; | |||
1400 | } | |||
1401 | ||||
1402 | cc_result Updater_MarkExecutable(void) { | |||
1403 | struct stat st; | |||
1404 | if (stat(UPDATE_FILE"ClassiCube.update", &st) == -1) return errno(*__errno()); | |||
1405 | ||||
1406 | st.st_mode |= S_IXUSR0000100; | |||
1407 | return chmod(UPDATE_FILE"ClassiCube.update", st.st_mode) == -1 ? errno(*__errno()) : 0; | |||
1408 | } | |||
1409 | ||||
1410 | cc_result Updater_SetNewBuildTime(cc_uint64 timestamp) { | |||
1411 | struct utimbuf times = { 0 }; | |||
1412 | times.modtime = timestamp; | |||
1413 | return utime(UPDATE_FILE"ClassiCube.update", ×) == -1 ? errno(*__errno()) : 0; | |||
1414 | } | |||
1415 | #endif | |||
1416 | ||||
1417 | ||||
1418 | /*########################################################################################################################* | |||
1419 | *-------------------------------------------------------Dynamic lib-------------------------------------------------------* | |||
1420 | *#########################################################################################################################*/ | |||
1421 | #if defined CC_BUILD_WIN | |||
1422 | const cc_string DynamicLib_Ext = String_FromConst(".dll"){ ".dll", (sizeof(".dll") - 1), (sizeof(".dll") - 1)}; | |||
1423 | ||||
1424 | void* DynamicLib_Load2(const cc_string* path) { | |||
1425 | TCHAR str[NATIVE_STR_LEN600]; | |||
1426 | Platform_EncodeString(str, path); | |||
1427 | return LoadLibrary(str); | |||
1428 | } | |||
1429 | ||||
1430 | void* DynamicLib_Get2(void* lib, const char* name) { | |||
1431 | return GetProcAddress((HMODULE)lib, name); | |||
1432 | } | |||
1433 | ||||
1434 | cc_bool DynamicLib_DescribeError(cc_string* dst) { | |||
1435 | cc_result res = GetLastError(); | |||
1436 | Platform_DescribeError(res, dst); | |||
1437 | String_Format1(dst, " (error %i)", &res); | |||
1438 | return true1; | |||
1439 | } | |||
1440 | #elif defined CC_BUILD_WEB | |||
1441 | void* DynamicLib_Load2(const cc_string* path) { return NULL((void*)0); } | |||
1442 | void* DynamicLib_Get2(void* lib, const char* name) { return NULL((void*)0); } | |||
1443 | cc_bool DynamicLib_DescribeError(cc_string* dst) { return false0; } | |||
1444 | #elif defined MAC_OS_X_VERSION_MIN_REQUIRED && (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) | |||
1445 | /* Really old mac OS versions don't have the dlopen/dlsym API */ | |||
1446 | const cc_string DynamicLib_Ext = String_FromConst(".dylib"){ ".dylib", (sizeof(".dylib") - 1), (sizeof(".dylib") - 1)}; | |||
1447 | ||||
1448 | void* DynamicLib_Load2(const cc_string* path) { | |||
1449 | char str[NATIVE_STR_LEN600]; | |||
1450 | Platform_EncodeString(str, path); | |||
1451 | return NSAddImage(str, NSADDIMAGE_OPTION_WITH_SEARCHING | | |||
1452 | NSADDIMAGE_OPTION_RETURN_ON_ERROR); | |||
1453 | } | |||
1454 | ||||
1455 | void* DynamicLib_Get2(void* lib, const char* name) { | |||
1456 | cc_string tmp; char tmpBuffer[128]; | |||
1457 | NSSymbol sym; | |||
1458 | String_InitArray_NT(tmp, tmpBuffer)tmp.buffer = tmpBuffer; tmp.length = 0; tmp.capacity = sizeof (tmpBuffer) - 1;; | |||
1459 | ||||
1460 | /* NS linker api rquires symbols to have a _ prefix */ | |||
1461 | String_Append(&tmp, '_'); | |||
1462 | String_AppendConst(&tmp, name); | |||
1463 | tmp.buffer[tmp.length] = '\0'; | |||
1464 | ||||
1465 | sym = NSLookupSymbolInImage(lib, tmp.buffer, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | | |||
1466 | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); | |||
1467 | return sym ? NSAddressOfSymbol(sym) : NULL((void*)0); | |||
1468 | } | |||
1469 | ||||
1470 | cc_bool DynamicLib_DescribeError(cc_string* dst) { | |||
1471 | NSLinkEditErrors err = 0; | |||
1472 | const char* name = ""; | |||
1473 | const char* msg = ""; | |||
1474 | int errNum = 0; | |||
1475 | ||||
1476 | NSLinkEditError(&err, &errNum, &name, &msg); | |||
1477 | String_Format4(dst, "%c in %c (%i, sys %i)", msg, name, &err, &errNum); | |||
1478 | return true1; | |||
1479 | } | |||
1480 | #elif defined CC_BUILD_POSIX | |||
1481 | #include <dlfcn.h> | |||
1482 | /* TODO: Should we use .bundle instead of .dylib? */ | |||
1483 | ||||
1484 | #ifdef CC_BUILD_OSX | |||
1485 | const cc_string DynamicLib_Ext = String_FromConst(".dylib"){ ".dylib", (sizeof(".dylib") - 1), (sizeof(".dylib") - 1)}; | |||
1486 | #else | |||
1487 | const cc_string DynamicLib_Ext = String_FromConst(".so"){ ".so", (sizeof(".so") - 1), (sizeof(".so") - 1)}; | |||
1488 | #endif | |||
1489 | ||||
1490 | void* DynamicLib_Load2(const cc_string* path) { | |||
1491 | char str[NATIVE_STR_LEN600]; | |||
1492 | Platform_EncodeString(str, path); | |||
1493 | return dlopen(str, RTLD_NOW2); | |||
1494 | } | |||
1495 | ||||
1496 | void* DynamicLib_Get2(void* lib, const char* name) { | |||
1497 | return dlsym(lib, name); | |||
1498 | } | |||
1499 | ||||
1500 | cc_bool DynamicLib_DescribeError(cc_string* dst) { | |||
1501 | char* err = dlerror(); | |||
1502 | if (err) String_AppendConst(dst, err); | |||
1503 | return err && err[0]; | |||
1504 | } | |||
1505 | #endif | |||
1506 | ||||
1507 | cc_result DynamicLib_Load(const cc_string* path, void** lib) { | |||
1508 | *lib = DynamicLib_Load2(path); | |||
1509 | return *lib == NULL((void*)0); | |||
1510 | } | |||
1511 | cc_result DynamicLib_Get(void* lib, const char* name, void** symbol) { | |||
1512 | *symbol = DynamicLib_Get2(lib, name); | |||
1513 | return *symbol == NULL((void*)0); | |||
1514 | } | |||
1515 | ||||
1516 | ||||
1517 | cc_bool DynamicLib_GetAll(void* lib, const struct DynamicLibSym* syms, int count) { | |||
1518 | int i, loaded = 0; | |||
1519 | void* addr; | |||
1520 | ||||
1521 | for (i = 0; i < count; i++) { | |||
1522 | addr = DynamicLib_Get2(lib, syms[i].name); | |||
1523 | if (addr) loaded++; | |||
1524 | *syms[i].symAddr = addr; | |||
1525 | } | |||
1526 | return loaded == count; | |||
1527 | } | |||
1528 | ||||
1529 | ||||
1530 | /*########################################################################################################################* | |||
1531 | *--------------------------------------------------------Platform---------------------------------------------------------* | |||
1532 | *#########################################################################################################################*/ | |||
1533 | #if defined CC_BUILD_WIN | |||
1534 | int Platform_EncodeString(void* data, const cc_string* src) { | |||
1535 | TCHAR* dst = (TCHAR*)data; | |||
1536 | int i; | |||
1537 | if (src->length > FILENAME_SIZE260) Logger_Abort("String too long to expand"); | |||
1538 | ||||
1539 | for (i = 0; i < src->length; i++) { | |||
1540 | *dst++ = Convert_CP437ToUnicode(src->buffer[i]); | |||
1541 | } | |||
1542 | *dst = '\0'; | |||
1543 | return src->length * 2; | |||
1544 | } | |||
1545 | ||||
1546 | void Platform_DecodeString(cc_string* dst, const void* data, int len) { | |||
1547 | #ifdef UNICODE | |||
1548 | String_AppendUtf16(dst, (const cc_unichar*)data, len * 2); | |||
1549 | #else | |||
1550 | String_DecodeCP1252(dst, (const cc_uint8*)data, len); | |||
1551 | #endif | |||
1552 | } | |||
1553 | ||||
1554 | static void Platform_InitStopwatch(void) { | |||
1555 | LARGE_INTEGER freq; | |||
1556 | sw_highRes = QueryPerformanceFrequency(&freq); | |||
1557 | ||||
1558 | if (sw_highRes) { | |||
1559 | sw_freqMul = 1000 * 1000; | |||
1560 | sw_freqDiv = freq.QuadPart; | |||
1561 | } else { sw_freqDiv = 10; } | |||
1562 | } | |||
1563 | ||||
1564 | typedef BOOL (WINAPI *FUNC_AttachConsole)(DWORD dwProcessId); | |||
1565 | static void AttachParentConsole(void) { | |||
1566 | static const cc_string kernel32 = String_FromConst("KERNEL32.DLL"){ "KERNEL32.DLL", (sizeof("KERNEL32.DLL") - 1), (sizeof("KERNEL32.DLL" ) - 1)}; | |||
1567 | FUNC_AttachConsole attach; | |||
1568 | void* lib; | |||
1569 | ||||
1570 | /* NOTE: Need to dynamically load, not supported on Windows 2000 */ | |||
1571 | if ((lib = DynamicLib_Load2(&kernel32))) { | |||
1572 | attach = (FUNC_AttachConsole)DynamicLib_Get2(lib, "AttachConsole"); | |||
1573 | if (attach) attach((DWORD)-1); /* ATTACH_PARENT_PROCESS */ | |||
1574 | } | |||
1575 | } | |||
1576 | ||||
1577 | void Platform_Init(void) { | |||
1578 | WSADATA wsaData; | |||
1579 | cc_result res; | |||
1580 | ||||
1581 | Platform_InitStopwatch(); | |||
1582 | heap = GetProcessHeap(); | |||
1583 | ||||
1584 | res = WSAStartup(MAKEWORD(2, 2), &wsaData); | |||
1585 | if (res) Logger_SysWarn(res, "starting WSA"); | |||
1586 | ||||
1587 | hasDebugger = IsDebuggerPresent(); | |||
1588 | /* For when user runs from command prompt */ | |||
1589 | AttachParentConsole(); | |||
1590 | ||||
1591 | conHandle = GetStdHandle(STD_OUTPUT_HANDLE); | |||
1592 | if (conHandle == INVALID_HANDLE_VALUE) conHandle = NULL((void*)0); | |||
1593 | } | |||
1594 | ||||
1595 | void Platform_Free(void) { | |||
1596 | WSACleanup(); | |||
1597 | HeapDestroy(heap); | |||
1598 | } | |||
1599 | ||||
1600 | cc_result Platform_Encrypt(const cc_string* key, const void* data, int len, cc_string* dst) { | |||
1601 | DATA_BLOB input, output; | |||
1602 | int i; | |||
1603 | input.cbData = len; input.pbData = (BYTE*)data; | |||
1604 | if (!CryptProtectData(&input, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, &output)) return GetLastError(); | |||
1605 | ||||
1606 | for (i = 0; i < output.cbData; i++) { | |||
1607 | String_Append(dst, output.pbData[i]); | |||
1608 | } | |||
1609 | LocalFree(output.pbData); | |||
1610 | return 0; | |||
1611 | } | |||
1612 | cc_result Platform_Decrypt(const cc_string* key, const void* data, int len, cc_string* dst) { | |||
1613 | DATA_BLOB input, output; | |||
1614 | int i; | |||
1615 | input.cbData = len; input.pbData = (BYTE*)data; | |||
1616 | if (!CryptUnprotectData(&input, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, &output)) return GetLastError(); | |||
1617 | ||||
1618 | for (i = 0; i < output.cbData; i++) { | |||
1619 | String_Append(dst, output.pbData[i]); | |||
1620 | } | |||
1621 | LocalFree(output.pbData); | |||
1622 | return 0; | |||
1623 | } | |||
1624 | ||||
1625 | cc_bool Platform_DescribeErrorExt(cc_result res, cc_string* dst, void* lib) { | |||
1626 | TCHAR chars[NATIVE_STR_LEN600]; | |||
1627 | DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; | |||
1628 | if (lib) flags |= FORMAT_MESSAGE_FROM_HMODULE; | |||
1629 | ||||
1630 | res = FormatMessage(flags, lib, res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |||
1631 | chars, NATIVE_STR_LEN600, NULL((void*)0)); | |||
1632 | if (!res) return false0; | |||
1633 | ||||
1634 | Platform_DecodeString(dst, chars, res); | |||
1635 | return true1; | |||
1636 | } | |||
1637 | ||||
1638 | cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { | |||
1639 | return Platform_DescribeErrorExt(res, dst, NULL((void*)0)); | |||
1640 | } | |||
1641 | #elif defined CC_BUILD_POSIX | |||
1642 | int Platform_EncodeString(void* data, const cc_string* src) { | |||
1643 | cc_uint8* dst = (cc_uint8*)data; | |||
1644 | cc_uint8* cur; | |||
1645 | int i, len = 0; | |||
1646 | if (src->length > FILENAME_SIZE260) Logger_Abort("String too long to expand"); | |||
1647 | ||||
1648 | for (i = 0; i < src->length; i++) { | |||
1649 | cur = dst + len; | |||
1650 | len += Convert_CP437ToUtf8(src->buffer[i], cur); | |||
1651 | } | |||
1652 | dst[len] = '\0'; | |||
1653 | return len; | |||
1654 | } | |||
1655 | ||||
1656 | void Platform_DecodeString(cc_string* dst, const void* data, int len) { | |||
1657 | String_AppendUtf8(dst, (const cc_uint8*)data, len); | |||
1658 | } | |||
1659 | ||||
1660 | static void Platform_InitPosix(void) { | |||
1661 | signal(SIGCHLD20, SIG_IGN(void (*)(int))1); | |||
1662 | /* So writing to closed socket doesn't raise SIGPIPE */ | |||
1663 | signal(SIGPIPE13, SIG_IGN(void (*)(int))1); | |||
1664 | /* Assume stopwatch is in nanoseconds */ | |||
1665 | /* Some platforms (e.g. macOS) override this */ | |||
1666 | sw_freqDiv = 1000; | |||
1667 | } | |||
1668 | void Platform_Free(void) { } | |||
1669 | ||||
1670 | cc_result Platform_Encrypt(const cc_string* key, const void* data, int len, cc_string* dst) { | |||
1671 | /* TODO: Is there a similar API for macOS/Linux? */ | |||
1672 | /* Fallback to NOT SECURE XOR. Prevents simple reading from options.txt */ | |||
1673 | const cc_uint8* src = data; | |||
1674 | cc_uint8 c; | |||
1675 | int i; | |||
1676 | ||||
1677 | for (i = 0; i < len; i++) { | |||
1678 | c = (cc_uint8)(src[i] ^ key->buffer[i % key->length] ^ 0x43); | |||
1679 | String_Append(dst, c); | |||
1680 | } | |||
1681 | return 0; | |||
1682 | } | |||
1683 | cc_result Platform_Decrypt(const cc_string* key, const void* data, int len, cc_string* dst) { | |||
1684 | /* TODO: Is there a similar API for macOS/Linux? */ | |||
1685 | return Platform_Encrypt(key, data, len, dst); | |||
1686 | } | |||
1687 | ||||
1688 | cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { | |||
1689 | char chars[NATIVE_STR_LEN600]; | |||
1690 | int len; | |||
1691 | ||||
1692 | /* For unrecognised error codes, strerror_r might return messages */ | |||
1693 | /* such as 'No error information', which is not very useful */ | |||
1694 | /* (could check errno here but quicker just to skip entirely) */ | |||
1695 | if (res >= 1000) return false0; | |||
1696 | ||||
1697 | len = strerror_r(res, chars, NATIVE_STR_LEN600); | |||
1698 | if (len == -1) return false0; | |||
1699 | ||||
1700 | len = String_CalcLen(chars, NATIVE_STR_LEN600); | |||
1701 | Platform_DecodeString(dst, chars, len); | |||
1702 | return true1; | |||
1703 | } | |||
1704 | ||||
1705 | #if defined CC_BUILD_OSX | |||
1706 | static void Platform_InitStopwatch(void) { | |||
1707 | mach_timebase_info_data_t tb = { 0 }; | |||
1708 | mach_timebase_info(&tb); | |||
1709 | ||||
1710 | sw_freqMul = tb.numer; | |||
1711 | /* tb.denom may be large, so multiplying by 1000 overflows 32 bits */ | |||
1712 | /* (one powerpc system had tb.denom of 33329426) */ | |||
1713 | sw_freqDiv = (cc_uint64)tb.denom * 1000; | |||
1714 | } | |||
1715 | ||||
1716 | void Platform_Init(void) { | |||
1717 | ProcessSerialNumber psn; /* TODO: kCurrentProcess */ | |||
1718 | Platform_InitPosix(); | |||
1719 | Platform_InitStopwatch(); | |||
1720 | ||||
1721 | /* NOTE: Call as soon as possible, otherwise can't click on dialog boxes. */ | |||
1722 | GetCurrentProcess(&psn); | |||
1723 | /* NOTE: TransformProcessType is macOS 10.3 or later */ | |||
1724 | TransformProcessType(&psn, kProcessTransformToForegroundApplication); | |||
1725 | } | |||
1726 | #elif defined CC_BUILD_WEB | |||
1727 | void Platform_Init(void) { | |||
1728 | char tmp[64+1] = { 0 }; | |||
1729 | EM_ASM( Module['websocket']['subprotocol'] = 'ClassiCube'; ); | |||
1730 | /* Check if an error occurred when pre-loading IndexedDB */ | |||
1731 | EM_ASM_({ if (window.cc_idbErr) stringToUTF8(window.cc_idbErr, $0, 64); }, tmp); | |||
1732 | ||||
1733 | EM_ASM({ | |||
1734 | Module.saveBlob = function(blob, name) { | |||
1735 | if (window.navigator.msSaveBlob) { | |||
1736 | window.navigator.msSaveBlob(blob, name); return; | |||
1737 | } | |||
1738 | var url = window.URL.createObjectURL(blob); | |||
1739 | var elem = document.createElement('a'); | |||
1740 | ||||
1741 | elem.href = url; | |||
1742 | elem.download = name; | |||
1743 | elem.style.display = 'none'; | |||
1744 | ||||
1745 | document.body.appendChild(elem); | |||
1746 | elem.click(); | |||
1747 | document.body.removeChild(elem); | |||
1748 | window.URL.revokeObjectURL(url); | |||
1749 | } | |||
1750 | }); | |||
1751 | ||||
1752 | if (!tmp[0]) return; | |||
1753 | Chat_Add1("&cError preloading IndexedDB: %c", tmp); | |||
1754 | Chat_AddRaw("&cPreviously saved settings/maps will be lost"); | |||
1755 | ||||
1756 | /* NOTE: You must pre-load IndexedDB before main() */ | |||
1757 | /* (because pre-loading only works asynchronously) */ | |||
1758 | /* If you don't, you'll get errors later trying to sync local to remote */ | |||
1759 | /* See doc/hosting-webclient.md for example preloading IndexedDB code */ | |||
1760 | } | |||
1761 | #else | |||
1762 | void Platform_Init(void) { Platform_InitPosix(); } | |||
1763 | #endif | |||
1764 | #endif /* CC_BUILD_POSIX */ | |||
1765 | ||||
1766 | ||||
1767 | /*########################################################################################################################* | |||
1768 | *--------------------------------------------------------Platform---------------------------------------------------------* | |||
1769 | *#########################################################################################################################*/ | |||
1770 | #if defined CC_BUILD_WIN | |||
1771 | static cc_string Platform_NextArg(STRING_REF cc_string* args) { | |||
1772 | cc_string arg; | |||
1773 | int end; | |||
1774 | ||||
1775 | /* get rid of leading spaces before arg */ | |||
1776 | while (args->length && args->buffer[0] == ' ') { | |||
1777 | *args = String_UNSAFE_SubstringAt(args, 1); | |||
1778 | } | |||
1779 | ||||
1780 | if (args->length && args->buffer[0] == '"') { | |||
1781 | /* "xy za" is used for arg with spaces */ | |||
1782 | *args = String_UNSAFE_SubstringAt(args, 1); | |||
1783 | end = String_IndexOf(args, '"')String_IndexOfAt(args, 0, '"'); | |||
1784 | } else { | |||
1785 | end = String_IndexOf(args, ' ')String_IndexOfAt(args, 0, ' '); | |||
1786 | } | |||
1787 | ||||
1788 | if (end == -1) { | |||
1789 | arg = *args; | |||
1790 | args->length = 0; | |||
1791 | } else { | |||
1792 | arg = String_UNSAFE_Substring(args, 0, end); | |||
1793 | *args = String_UNSAFE_SubstringAt(args, end + 1); | |||
1794 | } | |||
1795 | return arg; | |||
1796 | } | |||
1797 | ||||
1798 | int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { | |||
1799 | cc_string cmdArgs = String_FromReadonly(GetCommandLineA()); | |||
1800 | int i; | |||
1801 | Platform_NextArg(&cmdArgs); /* skip exe path */ | |||
1802 | ||||
1803 | for (i = 0; i < GAME_MAX_CMDARGS5; i++) { | |||
1804 | args[i] = Platform_NextArg(&cmdArgs); | |||
1805 | ||||
1806 | if (!args[i].length) break; | |||
1807 | } | |||
1808 | return i; | |||
1809 | } | |||
1810 | ||||
1811 | cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { | |||
1812 | TCHAR path[NATIVE_STR_LEN600 + 1]; | |||
1813 | int i, len; | |||
1814 | cc_result res = Process_RawGetExePath(path, &len); | |||
1815 | if (res) return res; | |||
1816 | ||||
1817 | /* Get rid of filename at end of directory */ | |||
1818 | for (i = len - 1; i >= 0; i--, len--) { | |||
1819 | if (path[i] == '/' || path[i] == '\\') break; | |||
1820 | } | |||
1821 | ||||
1822 | path[len] = '\0'; | |||
1823 | return SetCurrentDirectory(path) ? 0 : GetLastError(); | |||
1824 | } | |||
1825 | #elif defined CC_BUILD_WEB | |||
1826 | int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { | |||
1827 | int i, count; | |||
1828 | argc--; argv++; /* skip executable path argument */ | |||
1829 | ||||
1830 | count = min(argc, GAME_MAX_CMDARGS)((argc) < (5) ? (argc) : (5)); | |||
1831 | for (i = 0; i < count; i++) { args[i] = String_FromReadonly(argv[i]); } | |||
1832 | return count; | |||
1833 | } | |||
1834 | ||||
1835 | cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { | |||
1836 | return chdir("/classicube") == -1 ? errno(*__errno()) : 0; | |||
1837 | } | |||
1838 | #elif defined CC_BUILD_ANDROID | |||
1839 | int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { | |||
1840 | if (!gameArgs.length) return 0; | |||
1841 | return String_UNSAFE_Split(&gameArgs, ' ', args, GAME_MAX_CMDARGS5); | |||
1842 | } | |||
1843 | ||||
1844 | cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { | |||
1845 | cc_string dir; char dirBuffer[FILENAME_SIZE260 + 1]; | |||
1846 | String_InitArray_NT(dir, dirBuffer)dir.buffer = dirBuffer; dir.length = 0; dir.capacity = sizeof (dirBuffer) - 1;; | |||
1847 | ||||
1848 | JavaCall_Void_String("getExternalAppDir", &dir); | |||
1849 | dir.buffer[dir.length] = '\0'; | |||
1850 | Platform_Log1("EXTERNAL DIR: %s|", &dir); | |||
1851 | return chdir(dir.buffer) == -1 ? errno(*__errno()) : 0; | |||
1852 | } | |||
1853 | #elif defined CC_BUILD_POSIX | |||
1854 | int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { | |||
1855 | int i, count; | |||
1856 | argc--; argv++; /* skip executable path argument */ | |||
1857 | ||||
1858 | #ifdef CC_BUILD_OSX | |||
1859 | if (argc) { | |||
1860 | static const cc_string psn = String_FromConst("-psn_0_"){ "-psn_0_", (sizeof("-psn_0_") - 1), (sizeof("-psn_0_") - 1) }; | |||
1861 | cc_string arg0 = String_FromReadonly(argv[0]); | |||
1862 | if (String_CaselessStarts(&arg0, &psn)) { argc--; argv++; } | |||
1863 | } | |||
1864 | #endif | |||
1865 | ||||
1866 | count = min(argc, GAME_MAX_CMDARGS)((argc) < (5) ? (argc) : (5)); | |||
1867 | for (i = 0; i < count; i++) { | |||
1868 | if (argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2]) { | |||
1869 | --count; | |||
1870 | continue; | |||
1871 | } | |||
1872 | args[i] = String_FromReadonly(argv[i]); | |||
1873 | } | |||
1874 | return count; | |||
1875 | } | |||
1876 | ||||
1877 | ||||
1878 | cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { | |||
1879 | char path[NATIVE_STR_LEN600]; | |||
1880 | int i, len = 0; | |||
1881 | cc_result res; | |||
1882 | ||||
1883 | for (i = 1; i < argc; ++i) { | |||
1884 | if (argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2]) { | |||
1885 | defaultDirectory = argv[i]; | |||
1886 | break; | |||
1887 | } | |||
1888 | } | |||
1889 | ||||
1890 | if (defaultDirectory) { | |||
1891 | return chdir(defaultDirectory + 2) == -1 ? errno(*__errno()) : 0; | |||
1892 | } | |||
1893 | ||||
1894 | res = Process_RawGetExePath(path, &len); | |||
1895 | if (res) return res; | |||
1896 | ||||
1897 | /* get rid of filename at end of directory */ | |||
1898 | for (i = len - 1; i >= 0; i--, len--) { | |||
1899 | if (path[i] == '/') break; | |||
1900 | } | |||
1901 | ||||
1902 | #ifdef CC_BUILD_OSX | |||
1903 | static const cc_string bundle = String_FromConst(".app/Contents/MacOS/"){ ".app/Contents/MacOS/", (sizeof(".app/Contents/MacOS/") - 1 ), (sizeof(".app/Contents/MacOS/") - 1)}; | |||
1904 | cc_string raw = String_Init(path, len, 0); | |||
1905 | ||||
1906 | if (String_CaselessEnds(&raw, &bundle)) { | |||
1907 | len -= bundle.length; | |||
1908 | ||||
1909 | for (i = len - 1; i >= 0; i--, len--) { | |||
1910 | if (path[i] == '/') break; | |||
1911 | } | |||
1912 | } | |||
1913 | #endif | |||
1914 | ||||
1915 | path[len] = '\0'; | |||
1916 | return chdir(path) == -1 ? errno(*__errno()) : 0; | |||
1917 | } | |||
1918 | #endif | |||
1919 | ||||
1920 | /* Android java interop stuff */ | |||
1921 | #if defined CC_BUILD_ANDROID | |||
1922 | jclass App_Class; | |||
1923 | jobject App_Instance; | |||
1924 | JavaVM* VM_Ptr; | |||
1925 | ||||
1926 | /* JNI helpers */ | |||
1927 | cc_string JavaGetString(JNIEnv* env, jstring str, char* buffer) { | |||
1928 | const char* src; int len; | |||
1929 | cc_string dst; | |||
1930 | src = (*env)->GetStringUTFChars(env, str, NULL((void*)0)); | |||
1931 | len = (*env)->GetStringUTFLength(env, str); | |||
1932 | ||||
1933 | dst.buffer = buffer; | |||
1934 | dst.length = 0; | |||
1935 | dst.capacity = NATIVE_STR_LEN600; | |||
1936 | String_AppendUtf8(&dst, (const cc_uint8*)src, len); | |||
1937 | ||||
1938 | (*env)->ReleaseStringUTFChars(env, str, src); | |||
1939 | return dst; | |||
1940 | } | |||
1941 | ||||
1942 | jobject JavaMakeString(JNIEnv* env, const cc_string* str) { | |||
1943 | cc_uint8 tmp[2048 + 4]; | |||
1944 | cc_uint8* cur; | |||
1945 | int i, len = 0; | |||
1946 | ||||
1947 | for (i = 0; i < str->length && len < 2048; i++) { | |||
1948 | cur = tmp + len; | |||
1949 | len += Convert_CP437ToUtf8(str->buffer[i], cur); | |||
1950 | } | |||
1951 | tmp[len] = '\0'; | |||
1952 | return (*env)->NewStringUTF(env, (const char*)tmp); | |||
1953 | } | |||
1954 | ||||
1955 | jbyteArray JavaMakeBytes(JNIEnv* env, const cc_uint8* src, cc_uint32 len) { | |||
1956 | if (!len) return NULL((void*)0); | |||
1957 | jbyteArray arr = (*env)->NewByteArray(env, len); | |||
1958 | (*env)->SetByteArrayRegion(env, arr, 0, len, src); | |||
1959 | return arr; | |||
1960 | } | |||
1961 | ||||
1962 | void JavaCallVoid(JNIEnv* env, const char* name, const char* sig, jvalue* args) { | |||
1963 | jmethodID method = (*env)->GetMethodID(env, App_Class, name, sig); | |||
1964 | (*env)->CallVoidMethodA(env, App_Instance, method, args); | |||
1965 | } | |||
1966 | ||||
1967 | jint JavaCallInt(JNIEnv* env, const char* name, const char* sig, jvalue* args) { | |||
1968 | jmethodID method = (*env)->GetMethodID(env, App_Class, name, sig); | |||
1969 | return (*env)->CallIntMethodA(env, App_Instance, method, args); | |||
1970 | } | |||
1971 | ||||
1972 | jlong JavaCallLong(JNIEnv* env, const char* name, const char* sig, jvalue* args) { | |||
1973 | jmethodID method = (*env)->GetMethodID(env, App_Class, name, sig); | |||
1974 | return (*env)->CallLongMethodA(env, App_Instance, method, args); | |||
1975 | } | |||
1976 | ||||
1977 | jfloat JavaCallFloat(JNIEnv* env, const char* name, const char* sig, jvalue* args) { | |||
1978 | jmethodID method = (*env)->GetMethodID(env, App_Class, name, sig); | |||
1979 | return (*env)->CallFloatMethodA(env, App_Instance, method, args); | |||
1980 | } | |||
1981 | ||||
1982 | jobject JavaCallObject(JNIEnv* env, const char* name, const char* sig, jvalue* args) { | |||
1983 | jmethodID method = (*env)->GetMethodID(env, App_Class, name, sig); | |||
1984 | return (*env)->CallObjectMethodA(env, App_Instance, method, args); | |||
1985 | } | |||
1986 | ||||
1987 | void JavaCall_String_Void(const char* name, const cc_string* value) { | |||
1988 | JNIEnv* env; | |||
1989 | jvalue args[1]; | |||
1990 | JavaGetCurrentEnv(env); | |||
1991 | ||||
1992 | args[0].l = JavaMakeString(env, value); | |||
1993 | JavaCallVoid(env, name, "(Ljava/lang/String;)V", args); | |||
1994 | (*env)->DeleteLocalRef(env, args[0].l); | |||
1995 | } | |||
1996 | ||||
1997 | static void ReturnString(JNIEnv* env, jobject obj, cc_string* dst) { | |||
1998 | const jchar* src; | |||
1999 | jsize len; | |||
2000 | if (!obj) return; | |||
2001 | ||||
2002 | src = (*env)->GetStringChars(env, obj, NULL((void*)0)); | |||
2003 | len = (*env)->GetStringLength(env, obj); | |||
2004 | String_AppendUtf16(dst, src, len * 2); | |||
2005 | (*env)->ReleaseStringChars(env, obj, src); | |||
2006 | (*env)->DeleteLocalRef(env, obj); | |||
2007 | } | |||
2008 | ||||
2009 | void JavaCall_Void_String(const char* name, cc_string* dst) { | |||
2010 | JNIEnv* env; | |||
2011 | jobject obj; | |||
2012 | JavaGetCurrentEnv(env); | |||
2013 | ||||
2014 | obj = JavaCallObject(env, name, "()Ljava/lang/String;", NULL((void*)0)); | |||
2015 | ReturnString(env, obj, dst); | |||
2016 | } | |||
2017 | ||||
2018 | void JavaCall_String_String(const char* name, const cc_string* arg, cc_string* dst) { | |||
2019 | JNIEnv* env; | |||
2020 | jobject obj; | |||
2021 | jvalue args[1]; | |||
2022 | JavaGetCurrentEnv(env); | |||
2023 | ||||
2024 | args[0].l = JavaMakeString(env, arg); | |||
2025 | obj = JavaCallObject(env, name, "(Ljava/lang/String;)Ljava/lang/String;", args); | |||
2026 | ReturnString(env, obj, dst); | |||
2027 | (*env)->DeleteLocalRef(env, args[0].l); | |||
2028 | } | |||
2029 | #endif |