Upload 2.0.2
[physicsfs] / platform / pocketpc.c
1 /*
2  * PocketPC support routines for PhysicsFS.
3  *
4  * Please see the file LICENSE.txt in the source's root directory.
5  *
6  *  This file written by Ryan C. Gordon.
7  */
8
9 #define __PHYSICSFS_INTERNAL__
10 #include "physfs_platforms.h"
11
12 #ifdef PHYSFS_PLATFORM_POCKETPC
13
14 #include <stdio.h>
15 #include <windows.h>
16
17 #include "physfs_internal.h"
18
19 #define INVALID_FILE_ATTRIBUTES  0xFFFFFFFF
20 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
21 typedef struct
22 {
23     HANDLE handle;
24     int readonly;
25 } winCEfile;
26
27
28 const char *__PHYSFS_platformDirSeparator = "\\";
29 static char *userDir = NULL;
30
31 /*
32  * Figure out what the last failing Win32 API call was, and
33  *  generate a human-readable string for the error message.
34  *
35  * The return value is a static buffer that is overwritten with
36  *  each call to this function.
37  */
38 static const char *win32strerror(void)
39 {
40     static TCHAR msgbuf[255];
41     TCHAR *ptr = msgbuf;
42
43     FormatMessage(
44         FORMAT_MESSAGE_FROM_SYSTEM |
45         FORMAT_MESSAGE_IGNORE_INSERTS,
46         NULL,
47         GetLastError(),
48         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
49         msgbuf,
50         sizeof (msgbuf) / sizeof (TCHAR),
51         NULL 
52         );
53
54     /* chop off newlines. */
55     for (ptr = msgbuf; *ptr; ptr++)
56     {
57         if ((*ptr == '\n') || (*ptr == '\r'))
58         {
59             *ptr = ' ';
60             break;
61         } /* if */
62     } /* for */
63
64     return((const char *) msgbuf);
65 } /* win32strerror */
66
67
68 /* !!! FIXME: need to check all of these for NULLs. */
69 #define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \
70     if (str == NULL) \
71         w_assignto = NULL; \
72     else { \
73         const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \
74         w_assignto = (char *) __PHYSFS_smallAlloc(len); \
75         PHYSFS_uc2fromutf8(str, (PHYSFS_uint16 *) w_assignto, len); \
76     } \
77 } \
78
79
80 static char *getExePath()
81 {
82     DWORD buflen;
83     int success = 0;
84     TCHAR *ptr = NULL;
85     TCHAR *retval = (TCHAR*) allocator.Malloc(sizeof (TCHAR) * (MAX_PATH + 1));
86     char *charretval;
87     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
88
89     retval[0] = _T('\0');
90     /* !!! FIXME: don't preallocate here? */
91     /* !!! FIXME: use smallAlloc? */
92     buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1);
93     if (buflen <= 0)
94         __PHYSFS_setError(win32strerror());
95     else
96     {
97         retval[buflen] = '\0';  /* does API always null-terminate this? */
98         ptr = retval+buflen;
99         while( ptr != retval )
100         {
101             if( *ptr != _T('\\') )
102                 *ptr-- = _T('\0');
103             else
104                 break;
105         } /* while */
106         success = 1;
107     } /* else */
108
109     if (!success)
110     {
111         allocator.Free(retval);
112         return(NULL);  /* physfs error message will be set, above. */
113     } /* if */
114
115     buflen = (buflen * 4) + 1;
116     charretval = (char *) allocator.Malloc(buflen);
117     if (charretval != NULL)
118         PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) retval, charretval, buflen);
119     allocator.Free(retval);
120     return(charretval);   /* w00t. */
121 } /* getExePath */
122
123
124 int __PHYSFS_platformInit(void)
125 {
126     userDir = getExePath();
127     BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* failed? */
128     return(1);  /* always succeed. */
129 } /* __PHYSFS_platformInit */
130
131
132 int __PHYSFS_platformDeinit(void)
133 {
134     allocator.Free(userDir);
135     return(1);  /* always succeed. */
136 } /* __PHYSFS_platformDeinit */
137
138
139 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
140 {
141     /* no-op on this platform. */
142 } /* __PHYSFS_platformDetectAvailableCDs */
143
144
145 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
146 {
147     return(getExePath());
148 } /* __PHYSFS_platformCalcBaseDir */
149
150
151 char *__PHYSFS_platformGetUserName(void)
152 {
153     BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL);
154 } /* __PHYSFS_platformGetUserName */
155
156
157 char *__PHYSFS_platformGetUserDir(void)
158 {
159     return userDir;
160     BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL);
161 } /* __PHYSFS_platformGetUserDir */
162
163
164 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
165 {
166     return(1);  /* single threaded. */
167 } /* __PHYSFS_platformGetThreadID */
168
169
170 int __PHYSFS_platformExists(const char *fname)
171 {
172     int retval = 0;
173     wchar_t *w_fname = NULL;
174
175     UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
176     if (w_fname != NULL)
177         retval = (GetFileAttributes(w_fname) != INVALID_FILE_ATTRIBUTES);
178     __PHYSFS_smallFree(w_fname);
179
180     return(retval);
181 } /* __PHYSFS_platformExists */
182
183
184 int __PHYSFS_platformIsSymLink(const char *fname)
185 {
186     BAIL_MACRO(ERR_NOT_IMPLEMENTED, 0);
187 } /* __PHYSFS_platformIsSymlink */
188
189
190 int __PHYSFS_platformIsDirectory(const char *fname)
191 {
192     int retval = 0;
193     wchar_t *w_fname = NULL;
194
195     UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
196     if (w_fname != NULL)
197         retval = ((GetFileAttributes(w_fname) & FILE_ATTRIBUTE_DIRECTORY) != 0);
198     __PHYSFS_smallFree(w_fname);
199
200     return(retval);
201 } /* __PHYSFS_platformIsDirectory */
202
203
204 char *__PHYSFS_platformCvtToDependent(const char *prepend,
205                                       const char *dirName,
206                                       const char *append)
207 {
208     int len = ((prepend) ? strlen(prepend) : 0) +
209     ((append) ? strlen(append) : 0) +
210     strlen(dirName) + 1;
211     char *retval = (char *) allocator.Malloc(len);
212     char *p;
213
214     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
215
216     if (prepend)
217         strcpy(retval, prepend);
218     else
219         retval[0] = '\0';
220
221     strcat(retval, dirName);
222
223     if (append)
224         strcat(retval, append);
225
226     for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
227         *p = '\\';
228
229     return(retval);
230 } /* __PHYSFS_platformCvtToDependent */
231
232
233 static int doEnumCallback(const wchar_t *w_fname)
234 {
235     const PHYSFS_uint64 len = (PHYSFS_uint64) ((wcslen(w_fname) * 4) + 1);
236     char *str = (char *) __PHYSFS_smallAlloc(len);
237     PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) w_fname, str, len);
238     callback(callbackdata, origdir, str);
239     __PHYSFS_smallFree(str);
240     return 1;
241 } /* doEnumCallback */
242
243
244 void __PHYSFS_platformEnumerateFiles(const char *dirname,
245                                      int omitSymLinks,
246                                      PHYSFS_EnumFilesCallback callback,
247                                      const char *origdir,
248                                      void *callbackdata)
249 {
250     HANDLE dir;
251     WIN32_FIND_DATA ent;
252     char *SearchPath;
253     wchar_t *w_SearchPath;
254     size_t len = strlen(dirname);
255
256     /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
257     SearchPath = (char *) __PHYSFS_smallAlloc(len + 3);
258     BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL);    
259
260     /* Copy current dirname */
261     strcpy(SearchPath, dirname);
262
263     /* if there's no '\\' at the end of the path, stick one in there. */
264     if (SearchPath[len - 1] != '\\')
265     {
266         SearchPath[len++] = '\\';
267         SearchPath[len] = '\0';
268     } /* if */
269
270     /* Append the "*" to the end of the string */
271     strcat(SearchPath, "*");
272
273     UTF8_TO_UNICODE_STACK_MACRO(w_SearchPath, SearchPath);
274     __PHYSFS_smallFree(SearchPath);
275     dir = FindFirstFile(w_SearchPath, &ent);
276     __PHYSFS_smallFree(w_SearchPath);
277
278     if (dir == INVALID_HANDLE_VALUE)
279         return;
280
281     do
282     {
283         const char *str = NULL;
284
285         if (wcscmp(ent.cFileName, L".") == 0)
286             continue;
287
288         if (wcscmp(ent.cFileName, L"..") == 0)
289             continue;
290
291         if (!doEnumCallback(ent.cFileName))
292             break;
293     } while (FindNextFile(dir, &ent) != 0);
294
295     FindClose(dir);
296 } /* __PHYSFS_platformEnumerateFiles */
297
298
299 char *__PHYSFS_platformCurrentDir(void)
300 {
301     return("\\");
302 } /* __PHYSFS_platformCurrentDir */
303
304
305 char *__PHYSFS_platformRealPath(const char *path)
306 {
307     char *retval = (char *) allocator.Malloc(strlen(path) + 1);
308     strcpy(retval,path);
309     return(retval);
310 } /* __PHYSFS_platformRealPath */
311
312
313 int __PHYSFS_platformMkDir(const char *path)
314 {
315     int retval = 0;
316     wchar_t *w_path = NULL;
317     UTF8_TO_UNICODE_STACK_MACRO(w_path, path);
318     if (w_path != NULL)
319     {
320         retval = CreateDirectory(w_path, NULL);
321         __PHYSFS_smallFree(w_fname);
322     } /* if */
323     return(retval);
324 } /* __PHYSFS_platformMkDir */
325
326
327 static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
328 {
329     HANDLE fileHandle;
330     winCEfile *retval;
331     wchar_t *w_fname = NULL;
332
333     UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
334     fileHandle = CreateFile(w_fname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE,
335                             NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
336     __PHYSFS_smallFree(w_fname);
337
338     BAIL_IF_MACRO(fileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
339
340     retval = (winCEfile *) allocator.Malloc(sizeof (winCEfile));
341     if (retval == NULL)
342     {
343         CloseHandle(fileHandle);
344         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
345     } /* if */
346
347     retval->readonly = rdonly;
348     retval->handle = fileHandle;
349     return(retval);
350 } /* doOpen */
351
352
353 void *__PHYSFS_platformOpenRead(const char *filename)
354 {
355     return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1));
356 } /* __PHYSFS_platformOpenRead */
357
358
359 void *__PHYSFS_platformOpenWrite(const char *filename)
360 {
361     return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0));
362 } /* __PHYSFS_platformOpenWrite */
363
364
365 void *__PHYSFS_platformOpenAppend(const char *filename)
366 {
367     void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
368     if (retval != NULL)
369     {
370         HANDLE h = ((winCEfile *) retval)->handle;
371         if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
372         {
373             const char *err = win32strerror();
374             CloseHandle(h);
375             allocator.Free(retval);
376             BAIL_MACRO(err, NULL);
377         } /* if */
378     } /* if */
379
380     return(retval);
381
382 } /* __PHYSFS_platformOpenAppend */
383
384
385 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
386                                     PHYSFS_uint32 size, PHYSFS_uint32 count)
387 {
388     HANDLE Handle = ((winCEfile *) opaque)->handle;
389     DWORD CountOfBytesRead;
390     PHYSFS_sint64 retval;
391
392     /* Read data from the file */
393     /*!!! - uint32 might be a greater # than DWORD */
394     if (!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL))
395     {
396         retval = -1;
397     } /* if */
398     else
399     {
400         /* Return the number of "objects" read. */
401         /* !!! - What if not the right amount of bytes was read to make an object? */
402         retval = CountOfBytesRead / size;
403     } /* else */
404
405     return(retval);
406
407 } /* __PHYSFS_platformRead */
408
409
410 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
411                                      PHYSFS_uint32 size, PHYSFS_uint32 count)
412 {
413     HANDLE Handle = ((winCEfile *) opaque)->handle;
414     DWORD CountOfBytesWritten;
415     PHYSFS_sint64 retval;
416
417     /* Read data from the file */
418     /*!!! - uint32 might be a greater # than DWORD */
419     if (!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL))
420     {
421         retval = -1;
422     } /* if */
423     else
424     {
425         /* Return the number of "objects" read. */
426         /*!!! - What if not the right number of bytes was written? */
427         retval = CountOfBytesWritten / size;
428     } /* else */
429
430     return(retval);
431
432 } /* __PHYSFS_platformWrite */
433
434
435 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
436 {
437     HANDLE Handle = ((winCEfile *) opaque)->handle;
438     DWORD HighOrderPos;
439     DWORD rc;
440
441     /* Get the high order 32-bits of the position */
442     //HighOrderPos = HIGHORDER_UINT64(pos);
443     HighOrderPos = (unsigned long)(pos>>32);
444
445     /*!!! SetFilePointer needs a signed 64-bit value. */
446     /* Move pointer "pos" count from start of file */
447     rc = SetFilePointer(Handle, (unsigned long)(pos&0x00000000ffffffff),
448                         &HighOrderPos, FILE_BEGIN);
449
450     if ((rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
451     {
452         BAIL_MACRO(win32strerror(), 0);
453     }
454
455     return(1);  /* No error occured */
456 } /* __PHYSFS_platformSeek */
457
458
459 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
460 {
461     HANDLE Handle = ((winCEfile *) opaque)->handle;
462     DWORD HighPos = 0;
463     DWORD LowPos;
464     PHYSFS_sint64 retval;
465
466     /* Get current position */
467     LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
468     if ((LowPos == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
469     {
470         BAIL_MACRO(win32strerror(), 0);
471     } /* if */
472     else
473     {
474         /* Combine the high/low order to create the 64-bit position value */
475         retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
476         //assert(retval >= 0);
477     } /* else */
478
479     return(retval);
480 } /* __PHYSFS_platformTell */
481
482
483 PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
484 {
485     HANDLE Handle = ((winCEfile *) opaque)->handle;
486     DWORD SizeHigh;
487     DWORD SizeLow;
488     PHYSFS_sint64 retval;
489
490     SizeLow = GetFileSize(Handle, &SizeHigh);
491     if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
492     {
493         BAIL_MACRO(win32strerror(), -1);
494     } /* if */
495     else
496     {
497         /* Combine the high/low order to create the 64-bit position value */
498         retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
499         //assert(retval >= 0);
500     } /* else */
501
502     return(retval);
503 } /* __PHYSFS_platformFileLength */
504
505
506 int __PHYSFS_platformEOF(void *opaque)
507 {
508     PHYSFS_sint64 FilePosition;
509     int retval = 0;
510
511     /* Get the current position in the file */
512     if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0)
513     {
514         /* Non-zero if EOF is equal to the file length */
515         retval = FilePosition == __PHYSFS_platformFileLength(opaque);
516     } /* if */
517
518     return(retval);
519 } /* __PHYSFS_platformEOF */
520
521
522 int __PHYSFS_platformFlush(void *opaque)
523 {
524     winCEfile *fh = ((winCEfile *) opaque);
525     if (!fh->readonly)
526         BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), win32strerror(), 0);
527
528     return(1);
529 } /* __PHYSFS_platformFlush */
530
531
532 int __PHYSFS_platformClose(void *opaque)
533 {
534     HANDLE Handle = ((winCEfile *) opaque)->handle;
535     BAIL_IF_MACRO(!CloseHandle(Handle), win32strerror(), 0);
536     allocator.Free(opaque);
537     return(1);
538 } /* __PHYSFS_platformClose */
539
540
541 int __PHYSFS_platformDelete(const char *path)
542 {
543     wchar_t *w_path = NULL;
544     UTF8_TO_UNICODE_STACK_MACRO(w_path, path);
545
546     /* If filename is a folder */
547     if (GetFileAttributes(w_path) == FILE_ATTRIBUTE_DIRECTORY)
548     {
549         int retval = !RemoveDirectory(w_path);
550         __PHYSFS_smallFree(w_path);
551         BAIL_IF_MACRO(retval, win32strerror(), 0);
552     } /* if */
553     else
554     {
555         int retval = !DeleteFile(w_path);
556         __PHYSFS_smallFree(w_path);
557         BAIL_IF_MACRO(retval, win32strerror(), 0);
558     } /* else */
559
560     return(1);  /* if you got here, it worked. */
561 } /* __PHYSFS_platformDelete */
562
563
564 /*
565  * !!! FIXME: why aren't we using Critical Sections instead of Mutexes?
566  * !!! FIXME:  mutexes on Windows are for cross-process sync. CritSects are
567  * !!! FIXME:  mutexes for threads in a single process and are faster.
568  */
569 void *__PHYSFS_platformCreateMutex(void)
570 {
571     return((void *) CreateMutex(NULL, FALSE, NULL));
572 } /* __PHYSFS_platformCreateMutex */
573
574
575 void __PHYSFS_platformDestroyMutex(void *mutex)
576 {
577     CloseHandle((HANDLE) mutex);
578 } /* __PHYSFS_platformDestroyMutex */
579
580
581 int __PHYSFS_platformGrabMutex(void *mutex)
582 {
583     return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED);
584 } /* __PHYSFS_platformGrabMutex */
585
586
587 void __PHYSFS_platformReleaseMutex(void *mutex)
588 {
589     ReleaseMutex((HANDLE) mutex);
590 } /* __PHYSFS_platformReleaseMutex */
591
592
593 PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
594 {
595     BAIL_MACRO(ERR_NOT_IMPLEMENTED, -1);
596 } /* __PHYSFS_platformGetLastModTime */
597
598
599 /* !!! FIXME: Don't use C runtime for allocators? */
600 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
601 {
602     return(0);  /* just use malloc() and friends. */
603 } /* __PHYSFS_platformSetDefaultAllocator */
604
605 #endif  /* PHYSFS_PLATFORM_POCKETPC */
606
607 /* end of pocketpc.c ... */
608