SDL 1.2.13
[crawl:crawl-sdl.git] / src / file / SDL_rwops.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2006 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* This file provides a general interface for SDL to read and write
25    data sources.  It can easily be extended to files, memory, etc.
26 */
27
28 #include "SDL_endian.h"
29 #include "SDL_rwops.h"
30
31
32 #if defined(__WIN32__) && !defined(__SYMBIAN32__)
33
34 /* Functions to read/write Win32 API file pointers */
35 /* Will not use it on WinCE because stdio is buffered, it means
36    faster, and all stdio functions anyway are embedded in coredll.dll - 
37    the main wince dll*/
38
39 #define WINDOWS_LEAN_AND_MEAN
40 #include <windows.h>
41
42 #ifndef INVALID_SET_FILE_POINTER
43 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
44 #endif
45
46 #define READAHEAD_BUFFER_SIZE   1024
47
48 static int SDLCALL win32_file_open(SDL_RWops *context, const char *filename, const char *mode)
49 {
50 #ifndef _WIN32_WCE
51         UINT    old_error_mode;
52 #endif
53         HANDLE  h;
54         DWORD   r_right, w_right;
55         DWORD   must_exist, truncate;
56         int             a_mode;
57
58         if (!context)
59                 return -1; /* failed (invalid call) */
60                 
61         context->hidden.win32io.h = INVALID_HANDLE_VALUE; /* mark this as unusable */
62         context->hidden.win32io.buffer.data = NULL;
63         context->hidden.win32io.buffer.size = 0;
64         context->hidden.win32io.buffer.left = 0;
65
66         /* "r" = reading, file must exist */
67         /* "w" = writing, truncate existing, file may not exist */
68         /* "r+"= reading or writing, file must exist            */
69         /* "a" = writing, append file may not exist             */
70         /* "a+"= append + read, file may not exist              */
71         /* "w+" = read, write, truncate. file may not exist    */
72         
73         must_exist = ( SDL_strchr(mode,'r') != NULL ) ? OPEN_EXISTING : 0;
74         truncate   = ( SDL_strchr(mode,'w') != NULL ) ? CREATE_ALWAYS : 0;
75         r_right    = ( SDL_strchr(mode,'+') != NULL || must_exist ) ? GENERIC_READ : 0;
76         a_mode     = ( SDL_strchr(mode,'a') != NULL ) ? OPEN_ALWAYS : 0;
77         w_right    = ( a_mode || SDL_strchr(mode,'+') || truncate ) ? GENERIC_WRITE : 0;
78
79         if (!r_right && !w_right) /* inconsistent mode */
80                 return -1; /* failed (invalid call) */
81
82         context->hidden.win32io.buffer.data = (char *)SDL_malloc(READAHEAD_BUFFER_SIZE);
83         if (!context->hidden.win32io.buffer.data) {
84                 SDL_OutOfMemory();
85                 return -1;
86         }
87
88 #ifdef _WIN32_WCE
89         {
90                 size_t size = SDL_strlen(filename)+1;
91                 wchar_t *filenameW = SDL_stack_alloc(wchar_t, size);
92
93                 if ( MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, size) == 0 ) {
94                         SDL_stack_free(filenameW);
95                         SDL_free(context->hidden.win32io.buffer.data);
96                         context->hidden.win32io.buffer.data = NULL;
97                         SDL_SetError("Unable to convert filename to Unicode");
98                         return -1;
99                 }
100                 h = CreateFile(filenameW, (w_right|r_right), (w_right)? 0 : FILE_SHARE_READ, 
101                                            NULL, (must_exist|truncate|a_mode), FILE_ATTRIBUTE_NORMAL,NULL);
102                 SDL_stack_free(filenameW);
103         }
104 #else
105         /* Do not open a dialog box if failure */
106         old_error_mode = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
107
108         h = CreateFile(filename, (w_right|r_right), (w_right)? 0 : FILE_SHARE_READ, 
109                            NULL, (must_exist|truncate|a_mode), FILE_ATTRIBUTE_NORMAL,NULL);
110
111         /* restore old behaviour */
112         SetErrorMode(old_error_mode);
113 #endif /* _WIN32_WCE */
114
115         if (h==INVALID_HANDLE_VALUE) {
116                 SDL_free(context->hidden.win32io.buffer.data);
117                 context->hidden.win32io.buffer.data = NULL;
118                 SDL_SetError("Couldn't open %s",filename);
119                 return -2; /* failed (CreateFile) */
120         }
121         context->hidden.win32io.h = h;
122         context->hidden.win32io.append = a_mode;
123
124         return 0; /* ok */
125 }
126 static int SDLCALL win32_file_seek(SDL_RWops *context, int offset, int whence)
127 {
128         DWORD win32whence;
129         int   file_pos;
130         
131         if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE) {
132                 SDL_SetError("win32_file_seek: invalid context/file not opened");
133                 return -1;
134         }
135
136         /* FIXME: We may be able to satisfy the seek within buffered data */
137         if (whence == RW_SEEK_CUR && context->hidden.win32io.buffer.left) {
138                 offset -= context->hidden.win32io.buffer.left;
139     }
140     context->hidden.win32io.buffer.left = 0;
141
142         switch (whence) {
143                 case RW_SEEK_SET:               
144                         win32whence = FILE_BEGIN; break;
145                 case RW_SEEK_CUR:
146                         win32whence = FILE_CURRENT; break;
147                 case RW_SEEK_END:
148                         win32whence = FILE_END; break;
149                 default:
150                         SDL_SetError("win32_file_seek: Unknown value for 'whence'");                    
151                         return -1;
152         }
153
154         file_pos = SetFilePointer(context->hidden.win32io.h,offset,NULL,win32whence);
155
156         if ( file_pos != INVALID_SET_FILE_POINTER )
157                 return file_pos; /* success */
158         
159         SDL_Error(SDL_EFSEEK);
160         return -1; /* error */
161 }
162 static int SDLCALL win32_file_read(SDL_RWops *context, void *ptr, int size, int maxnum)
163 {
164         int             total_need; 
165         int             total_read = 0; 
166     int     read_ahead;
167         DWORD   byte_read;
168         
169         total_need = size*maxnum;
170         
171         if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE || total_need<=0 || !size)    
172                 return 0;
173
174     if (context->hidden.win32io.buffer.left > 0) {
175         void *data = (char *)context->hidden.win32io.buffer.data +
176                              context->hidden.win32io.buffer.size -
177                              context->hidden.win32io.buffer.left;
178         read_ahead = SDL_min(total_need, context->hidden.win32io.buffer.left); 
179         SDL_memcpy(ptr, data, read_ahead);
180         context->hidden.win32io.buffer.left -= read_ahead;
181
182         if (read_ahead == total_need) {
183             return maxnum;
184         }
185         ptr = (char *)ptr + read_ahead;
186         total_need -= read_ahead;       
187                 total_read += read_ahead;
188     }
189
190     if (total_need < READAHEAD_BUFFER_SIZE) {
191         if (!ReadFile(context->hidden.win32io.h,context->hidden.win32io.buffer.data,READAHEAD_BUFFER_SIZE,&byte_read,NULL)) {
192             SDL_Error(SDL_EFREAD);
193             return 0;
194         }
195         read_ahead = SDL_min(total_need, (int)byte_read);
196         SDL_memcpy(ptr, context->hidden.win32io.buffer.data, read_ahead);
197         context->hidden.win32io.buffer.size = byte_read;
198         context->hidden.win32io.buffer.left = byte_read-read_ahead;
199         total_read += read_ahead;
200     } else {
201         if (!ReadFile(context->hidden.win32io.h,ptr,total_need,&byte_read,NULL)) {
202             SDL_Error(SDL_EFREAD);
203             return 0;
204         }
205         total_read += byte_read;
206     }
207         return (total_read/size);
208 }
209 static int SDLCALL win32_file_write(SDL_RWops *context, const void *ptr, int size, int num)
210 {
211         
212         int             total_bytes; 
213         DWORD   byte_written,nwritten;
214         
215         total_bytes = size*num;
216
217         if (!context || context->hidden.win32io.h==INVALID_HANDLE_VALUE || total_bytes<=0 || !size)     
218                 return 0;
219
220         if (context->hidden.win32io.buffer.left) {
221                 SetFilePointer(context->hidden.win32io.h,-context->hidden.win32io.buffer.left,NULL,FILE_CURRENT);
222                 context->hidden.win32io.buffer.left = 0;
223         }
224
225         /* if in append mode, we must go to the EOF before write */
226         if (context->hidden.win32io.append) {
227                 if ( SetFilePointer(context->hidden.win32io.h,0L,NULL,FILE_END) == INVALID_SET_FILE_POINTER ) {
228                         SDL_Error(SDL_EFWRITE);
229                         return 0;
230                 }
231         }
232         
233         if (!WriteFile(context->hidden.win32io.h,ptr,total_bytes,&byte_written,NULL)) {
234                 SDL_Error(SDL_EFWRITE);
235                 return 0;
236         }
237         
238         nwritten = byte_written/size;
239         return nwritten;
240 }
241 static int SDLCALL win32_file_close(SDL_RWops *context)
242 {
243         
244         if ( context ) {                                                                
245                 if (context->hidden.win32io.h != INVALID_HANDLE_VALUE) {
246                         CloseHandle(context->hidden.win32io.h);
247                         context->hidden.win32io.h = INVALID_HANDLE_VALUE; /* to be sure */
248                 }
249                 if (context->hidden.win32io.buffer.data) {
250                         SDL_free(context->hidden.win32io.buffer.data);
251                         context->hidden.win32io.buffer.data = NULL;
252                 }
253                 SDL_FreeRW(context);
254         }
255         return(0);
256 }
257 #endif /* __WIN32__ */
258
259 #ifdef HAVE_STDIO_H
260
261 /* Functions to read/write stdio file pointers */
262
263 static int SDLCALL stdio_seek(SDL_RWops *context, int offset, int whence)
264 {
265         if ( fseek(context->hidden.stdio.fp, offset, whence) == 0 ) {
266                 return(ftell(context->hidden.stdio.fp));
267         } else {
268                 SDL_Error(SDL_EFSEEK);
269                 return(-1);
270         }
271 }
272 static int SDLCALL stdio_read(SDL_RWops *context, void *ptr, int size, int maxnum)
273 {
274         size_t nread;
275
276         nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); 
277         if ( nread == 0 && ferror(context->hidden.stdio.fp) ) {
278                 SDL_Error(SDL_EFREAD);
279         }
280         return(nread);
281 }
282 static int SDLCALL stdio_write(SDL_RWops *context, const void *ptr, int size, int num)
283 {
284         size_t nwrote;
285
286         nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
287         if ( nwrote == 0 && ferror(context->hidden.stdio.fp) ) {
288                 SDL_Error(SDL_EFWRITE);
289         }
290         return(nwrote);
291 }
292 static int SDLCALL stdio_close(SDL_RWops *context)
293 {
294         if ( context ) {
295                 if ( context->hidden.stdio.autoclose ) {
296                         /* WARNING:  Check the return value here! */
297                         fclose(context->hidden.stdio.fp);
298                 }
299                 SDL_FreeRW(context);
300         }
301         return(0);
302 }
303 #endif /* !HAVE_STDIO_H */
304
305 /* Functions to read/write memory pointers */
306
307 static int SDLCALL mem_seek(SDL_RWops *context, int offset, int whence)
308 {
309         Uint8 *newpos;
310
311         switch (whence) {
312                 case RW_SEEK_SET:
313                         newpos = context->hidden.mem.base+offset;
314                         break;
315                 case RW_SEEK_CUR:
316                         newpos = context->hidden.mem.here+offset;
317                         break;
318                 case RW_SEEK_END:
319                         newpos = context->hidden.mem.stop+offset;
320                         break;
321                 default:
322                         SDL_SetError("Unknown value for 'whence'");
323                         return(-1);
324         }
325         if ( newpos < context->hidden.mem.base ) {
326                 newpos = context->hidden.mem.base;
327         }
328         if ( newpos > context->hidden.mem.stop ) {
329                 newpos = context->hidden.mem.stop;
330         }
331         context->hidden.mem.here = newpos;
332         return(context->hidden.mem.here-context->hidden.mem.base);
333 }
334 static int SDLCALL mem_read(SDL_RWops *context, void *ptr, int size, int maxnum)
335 {
336         size_t total_bytes;
337         size_t mem_available;
338
339         total_bytes = (maxnum * size);
340         if ( (maxnum <= 0) || (size <= 0) || ((total_bytes / maxnum) != (size_t) size) ) {
341                 return 0;
342         }
343
344         mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
345         if (total_bytes > mem_available) {
346                 total_bytes = mem_available;
347         }
348
349         SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
350         context->hidden.mem.here += total_bytes;
351
352         return (total_bytes / size);
353 }
354 static int SDLCALL mem_write(SDL_RWops *context, const void *ptr, int size, int num)
355 {
356         if ( (context->hidden.mem.here + (num*size)) > context->hidden.mem.stop ) {
357                 num = (context->hidden.mem.stop-context->hidden.mem.here)/size;
358         }
359         SDL_memcpy(context->hidden.mem.here, ptr, num*size);
360         context->hidden.mem.here += num*size;
361         return(num);
362 }
363 static int SDLCALL mem_writeconst(SDL_RWops *context, const void *ptr, int size, int num)
364 {
365         SDL_SetError("Can't write to read-only memory");
366         return(-1);
367 }
368 static int SDLCALL mem_close(SDL_RWops *context)
369 {
370         if ( context ) {
371                 SDL_FreeRW(context);
372         }
373         return(0);
374 }
375
376
377 /* Functions to create SDL_RWops structures from various data sources */
378
379 #ifdef __MACOS__
380 /*
381  * translate unix-style slash-separated filename to mac-style colon-separated
382  * name; return malloced string
383  */
384 static char *unix_to_mac(const char *file)
385 {
386         int flen = SDL_strlen(file);
387         char *path = SDL_malloc(flen + 2);
388         const char *src = file;
389         char *dst = path;
390         if(*src == '/') {
391                 /* really depends on filesystem layout, hope for the best */
392                 src++;
393         } else {
394                 /* Check if this is a MacOS path to begin with */
395                 if(*src != ':')
396                         *dst++ = ':';   /* relative paths begin with ':' */
397         }
398         while(src < file + flen) {
399                 const char *end = SDL_strchr(src, '/');
400                 int len;
401                 if(!end)
402                         end = file + flen; /* last component */
403                 len = end - src;
404                 if(len == 0 || (len == 1 && src[0] == '.')) {
405                         /* remove repeated slashes and . */
406                 } else {
407                         if(len == 2 && src[0] == '.' && src[1] == '.') {
408                                 /* replace .. with the empty string */
409                         } else {
410                                 SDL_memcpy(dst, src, len);
411                                 dst += len;
412                         }
413                         if(end < file + flen)
414                                 *dst++ = ':';
415                 }
416                 src = end + 1;
417         }
418         *dst++ = '\0';
419         return path;
420 }
421 #endif /* __MACOS__ */
422
423 SDL_RWops *SDL_RWFromFile(const char *file, const char *mode)
424 {
425         SDL_RWops *rwops = NULL;
426 #ifdef HAVE_STDIO_H
427         FILE *fp = NULL;
428 #endif
429         if ( !file || !*file || !mode || !*mode ) {
430                 SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
431                 return NULL;
432         }
433
434 #if defined(__WIN32__) && !defined(__SYMBIAN32__)
435         rwops = SDL_AllocRW();
436         if (!rwops)
437                 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
438         if (win32_file_open(rwops,file,mode) < 0) {
439                 SDL_FreeRW(rwops);
440                 return NULL;
441         }       
442         rwops->seek  = win32_file_seek;
443         rwops->read  = win32_file_read;
444         rwops->write = win32_file_write;
445         rwops->close = win32_file_close;
446
447 #elif HAVE_STDIO_H
448
449 #ifdef __MACOS__
450         {
451                 char *mpath = unix_to_mac(file);
452                 fp = fopen(mpath, mode);
453                 SDL_free(mpath);
454         }
455 #else
456         fp = fopen(file, mode);
457 #endif
458         if ( fp == NULL ) {
459                 SDL_SetError("Couldn't open %s", file);
460         } else {
461                 rwops = SDL_RWFromFP(fp, 1);
462         }
463 #else
464         SDL_SetError("SDL not compiled with stdio support");
465 #endif /* !HAVE_STDIO_H */
466
467         return(rwops);
468 }
469
470 #ifdef HAVE_STDIO_H
471 SDL_RWops *SDL_RWFromFP(FILE *fp, int autoclose)
472 {
473         SDL_RWops *rwops = NULL;
474
475         rwops = SDL_AllocRW();
476         if ( rwops != NULL ) {
477                 rwops->seek = stdio_seek;
478                 rwops->read = stdio_read;
479                 rwops->write = stdio_write;
480                 rwops->close = stdio_close;
481                 rwops->hidden.stdio.fp = fp;
482                 rwops->hidden.stdio.autoclose = autoclose;
483         }
484         return(rwops);
485 }
486 #endif /* HAVE_STDIO_H */
487
488 SDL_RWops *SDL_RWFromMem(void *mem, int size)
489 {
490         SDL_RWops *rwops;
491
492         rwops = SDL_AllocRW();
493         if ( rwops != NULL ) {
494                 rwops->seek = mem_seek;
495                 rwops->read = mem_read;
496                 rwops->write = mem_write;
497                 rwops->close = mem_close;
498                 rwops->hidden.mem.base = (Uint8 *)mem;
499                 rwops->hidden.mem.here = rwops->hidden.mem.base;
500                 rwops->hidden.mem.stop = rwops->hidden.mem.base+size;
501         }
502         return(rwops);
503 }
504
505 SDL_RWops *SDL_RWFromConstMem(const void *mem, int size)
506 {
507         SDL_RWops *rwops;
508
509         rwops = SDL_AllocRW();
510         if ( rwops != NULL ) {
511                 rwops->seek = mem_seek;
512                 rwops->read = mem_read;
513                 rwops->write = mem_writeconst;
514                 rwops->close = mem_close;
515                 rwops->hidden.mem.base = (Uint8 *)mem;
516                 rwops->hidden.mem.here = rwops->hidden.mem.base;
517                 rwops->hidden.mem.stop = rwops->hidden.mem.base+size;
518         }
519         return(rwops);
520 }
521
522 SDL_RWops *SDL_AllocRW(void)
523 {
524         SDL_RWops *area;
525
526         area = (SDL_RWops *)SDL_malloc(sizeof *area);
527         if ( area == NULL ) {
528                 SDL_OutOfMemory();
529         }
530         return(area);
531 }
532
533 void SDL_FreeRW(SDL_RWops *area)
534 {
535         SDL_free(area);
536 }
537
538 /* Functions for dynamically reading and writing endian-specific values */
539
540 Uint16 SDL_ReadLE16 (SDL_RWops *src)
541 {
542         Uint16 value;
543
544         SDL_RWread(src, &value, (sizeof value), 1);
545         return(SDL_SwapLE16(value));
546 }
547 Uint16 SDL_ReadBE16 (SDL_RWops *src)
548 {
549         Uint16 value;
550
551         SDL_RWread(src, &value, (sizeof value), 1);
552         return(SDL_SwapBE16(value));
553 }
554 Uint32 SDL_ReadLE32 (SDL_RWops *src)
555 {
556         Uint32 value;
557
558         SDL_RWread(src, &value, (sizeof value), 1);
559         return(SDL_SwapLE32(value));
560 }
561 Uint32 SDL_ReadBE32 (SDL_RWops *src)
562 {
563         Uint32 value;
564
565         SDL_RWread(src, &value, (sizeof value), 1);
566         return(SDL_SwapBE32(value));
567 }
568 Uint64 SDL_ReadLE64 (SDL_RWops *src)
569 {
570         Uint64 value;
571
572         SDL_RWread(src, &value, (sizeof value), 1);
573         return(SDL_SwapLE64(value));
574 }
575 Uint64 SDL_ReadBE64 (SDL_RWops *src)
576 {
577         Uint64 value;
578
579         SDL_RWread(src, &value, (sizeof value), 1);
580         return(SDL_SwapBE64(value));
581 }
582
583 int SDL_WriteLE16 (SDL_RWops *dst, Uint16 value)
584 {
585         value = SDL_SwapLE16(value);
586         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
587 }
588 int SDL_WriteBE16 (SDL_RWops *dst, Uint16 value)
589 {
590         value = SDL_SwapBE16(value);
591         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
592 }
593 int SDL_WriteLE32 (SDL_RWops *dst, Uint32 value)
594 {
595         value = SDL_SwapLE32(value);
596         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
597 }
598 int SDL_WriteBE32 (SDL_RWops *dst, Uint32 value)
599 {
600         value = SDL_SwapBE32(value);
601         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
602 }
603 int SDL_WriteLE64 (SDL_RWops *dst, Uint64 value)
604 {
605         value = SDL_SwapLE64(value);
606         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
607 }
608 int SDL_WriteBE64 (SDL_RWops *dst, Uint64 value)
609 {
610         value = SDL_SwapBE64(value);
611         return(SDL_RWwrite(dst, &value, (sizeof value), 1));
612 }