2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
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.
28 #include "SDL_endian.h"
29 #include "SDL_rwops.h"
32 #if defined(__WIN32__) && !defined(__SYMBIAN32__)
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 -
39 #define WINDOWS_LEAN_AND_MEAN
42 #ifndef INVALID_SET_FILE_POINTER
43 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
46 #define READAHEAD_BUFFER_SIZE 1024
48 static int SDLCALL
win32_file_open(SDL_RWops
*context
, const char *filename
, const char *mode
)
54 DWORD r_right
, w_right
;
55 DWORD must_exist
, truncate
;
59 return -1; /* failed (invalid call) */
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;
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 */
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;
79 if (!r_right
&& !w_right
) /* inconsistent mode */
80 return -1; /* failed (invalid call) */
82 context
->hidden
.win32io
.buffer
.data
= (char *)SDL_malloc(READAHEAD_BUFFER_SIZE
);
83 if (!context
->hidden
.win32io
.buffer
.data
) {
90 size_t size
= SDL_strlen(filename
)+1;
91 wchar_t *filenameW
= SDL_stack_alloc(wchar_t, size
);
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");
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
);
105 /* Do not open a dialog box if failure */
106 old_error_mode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
|SEM_FAILCRITICALERRORS
);
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
);
111 /* restore old behaviour */
112 SetErrorMode(old_error_mode
);
113 #endif /* _WIN32_WCE */
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) */
121 context
->hidden
.win32io
.h
= h
;
122 context
->hidden
.win32io
.append
= a_mode
;
126 static int SDLCALL
win32_file_seek(SDL_RWops
*context
, int offset
, int whence
)
131 if (!context
|| context
->hidden
.win32io
.h
== INVALID_HANDLE_VALUE
) {
132 SDL_SetError("win32_file_seek: invalid context/file not opened");
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
;
140 context
->hidden
.win32io
.buffer
.left
= 0;
144 win32whence
= FILE_BEGIN
; break;
146 win32whence
= FILE_CURRENT
; break;
148 win32whence
= FILE_END
; break;
150 SDL_SetError("win32_file_seek: Unknown value for 'whence'");
154 file_pos
= SetFilePointer(context
->hidden
.win32io
.h
,offset
,NULL
,win32whence
);
156 if ( file_pos
!= INVALID_SET_FILE_POINTER
)
157 return file_pos
; /* success */
159 SDL_Error(SDL_EFSEEK
);
160 return -1; /* error */
162 static int SDLCALL
win32_file_read(SDL_RWops
*context
, void *ptr
, int size
, int maxnum
)
169 total_need
= size
*maxnum
;
171 if (!context
|| context
->hidden
.win32io
.h
== INVALID_HANDLE_VALUE
|| total_need
<=0 || !size
)
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
;
182 if (read_ahead
== total_need
) {
185 ptr
= (char *)ptr
+ read_ahead
;
186 total_need
-= read_ahead
;
187 total_read
+= read_ahead
;
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
);
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
;
201 if (!ReadFile(context
->hidden
.win32io
.h
,ptr
,total_need
,&byte_read
,NULL
)) {
202 SDL_Error(SDL_EFREAD
);
205 total_read
+= byte_read
;
207 return (total_read
/size
);
209 static int SDLCALL
win32_file_write(SDL_RWops
*context
, const void *ptr
, int size
, int num
)
213 DWORD byte_written
,nwritten
;
215 total_bytes
= size
*num
;
217 if (!context
|| context
->hidden
.win32io
.h
==INVALID_HANDLE_VALUE
|| total_bytes
<=0 || !size
)
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;
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
);
233 if (!WriteFile(context
->hidden
.win32io
.h
,ptr
,total_bytes
,&byte_written
,NULL
)) {
234 SDL_Error(SDL_EFWRITE
);
238 nwritten
= byte_written
/size
;
241 static int SDLCALL
win32_file_close(SDL_RWops
*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 */
249 if (context
->hidden
.win32io
.buffer
.data
) {
250 SDL_free(context
->hidden
.win32io
.buffer
.data
);
251 context
->hidden
.win32io
.buffer
.data
= NULL
;
257 #endif /* __WIN32__ */
261 /* Functions to read/write stdio file pointers */
263 static int SDLCALL
stdio_seek(SDL_RWops
*context
, int offset
, int whence
)
265 if ( fseek(context
->hidden
.stdio
.fp
, offset
, whence
) == 0 ) {
266 return(ftell(context
->hidden
.stdio
.fp
));
268 SDL_Error(SDL_EFSEEK
);
272 static int SDLCALL
stdio_read(SDL_RWops
*context
, void *ptr
, int size
, int maxnum
)
276 nread
= fread(ptr
, size
, maxnum
, context
->hidden
.stdio
.fp
);
277 if ( nread
== 0 && ferror(context
->hidden
.stdio
.fp
) ) {
278 SDL_Error(SDL_EFREAD
);
282 static int SDLCALL
stdio_write(SDL_RWops
*context
, const void *ptr
, int size
, int num
)
286 nwrote
= fwrite(ptr
, size
, num
, context
->hidden
.stdio
.fp
);
287 if ( nwrote
== 0 && ferror(context
->hidden
.stdio
.fp
) ) {
288 SDL_Error(SDL_EFWRITE
);
292 static int SDLCALL
stdio_close(SDL_RWops
*context
)
295 if ( context
->hidden
.stdio
.autoclose
) {
296 /* WARNING: Check the return value here! */
297 fclose(context
->hidden
.stdio
.fp
);
303 #endif /* !HAVE_STDIO_H */
305 /* Functions to read/write memory pointers */
307 static int SDLCALL
mem_seek(SDL_RWops
*context
, int offset
, int whence
)
313 newpos
= context
->hidden
.mem
.base
+offset
;
316 newpos
= context
->hidden
.mem
.here
+offset
;
319 newpos
= context
->hidden
.mem
.stop
+offset
;
322 SDL_SetError("Unknown value for 'whence'");
325 if ( newpos
< context
->hidden
.mem
.base
) {
326 newpos
= context
->hidden
.mem
.base
;
328 if ( newpos
> context
->hidden
.mem
.stop
) {
329 newpos
= context
->hidden
.mem
.stop
;
331 context
->hidden
.mem
.here
= newpos
;
332 return(context
->hidden
.mem
.here
-context
->hidden
.mem
.base
);
334 static int SDLCALL
mem_read(SDL_RWops
*context
, void *ptr
, int size
, int maxnum
)
337 size_t mem_available
;
339 total_bytes
= (maxnum
* size
);
340 if ( (maxnum
<= 0) || (size
<= 0) || ((total_bytes
/ maxnum
) != (size_t) size
) ) {
344 mem_available
= (context
->hidden
.mem
.stop
- context
->hidden
.mem
.here
);
345 if (total_bytes
> mem_available
) {
346 total_bytes
= mem_available
;
349 SDL_memcpy(ptr
, context
->hidden
.mem
.here
, total_bytes
);
350 context
->hidden
.mem
.here
+= total_bytes
;
352 return (total_bytes
/ size
);
354 static int SDLCALL
mem_write(SDL_RWops
*context
, const void *ptr
, int size
, int num
)
356 if ( (context
->hidden
.mem
.here
+ (num
*size
)) > context
->hidden
.mem
.stop
) {
357 num
= (context
->hidden
.mem
.stop
-context
->hidden
.mem
.here
)/size
;
359 SDL_memcpy(context
->hidden
.mem
.here
, ptr
, num
*size
);
360 context
->hidden
.mem
.here
+= num
*size
;
363 static int SDLCALL
mem_writeconst(SDL_RWops
*context
, const void *ptr
, int size
, int num
)
365 SDL_SetError("Can't write to read-only memory");
368 static int SDLCALL
mem_close(SDL_RWops
*context
)
377 /* Functions to create SDL_RWops structures from various data sources */
381 * translate unix-style slash-separated filename to mac-style colon-separated
382 * name; return malloced string
384 static char *unix_to_mac(const char *file
)
386 int flen
= SDL_strlen(file
);
387 char *path
= SDL_malloc(flen
+ 2);
388 const char *src
= file
;
391 /* really depends on filesystem layout, hope for the best */
394 /* Check if this is a MacOS path to begin with */
396 *dst
++ = ':'; /* relative paths begin with ':' */
398 while(src
< file
+ flen
) {
399 const char *end
= SDL_strchr(src
, '/');
402 end
= file
+ flen
; /* last component */
404 if(len
== 0 || (len
== 1 && src
[0] == '.')) {
405 /* remove repeated slashes and . */
407 if(len
== 2 && src
[0] == '.' && src
[1] == '.') {
408 /* replace .. with the empty string */
410 SDL_memcpy(dst
, src
, len
);
413 if(end
< file
+ flen
)
421 #endif /* __MACOS__ */
423 SDL_RWops
*SDL_RWFromFile(const char *file
, const char *mode
)
425 SDL_RWops
*rwops
= NULL
;
429 if ( !file
|| !*file
|| !mode
|| !*mode
) {
430 SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
434 #if defined(__WIN32__) && !defined(__SYMBIAN32__)
435 rwops
= SDL_AllocRW();
437 return NULL
; /* SDL_SetError already setup by SDL_AllocRW() */
438 if (win32_file_open(rwops
,file
,mode
) < 0) {
442 rwops
->seek
= win32_file_seek
;
443 rwops
->read
= win32_file_read
;
444 rwops
->write
= win32_file_write
;
445 rwops
->close
= win32_file_close
;
451 char *mpath
= unix_to_mac(file
);
452 fp
= fopen(mpath
, mode
);
456 fp
= fopen(file
, mode
);
459 SDL_SetError("Couldn't open %s", file
);
461 rwops
= SDL_RWFromFP(fp
, 1);
464 SDL_SetError("SDL not compiled with stdio support");
465 #endif /* !HAVE_STDIO_H */
471 SDL_RWops
*SDL_RWFromFP(FILE *fp
, int autoclose
)
473 SDL_RWops
*rwops
= NULL
;
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
;
486 #endif /* HAVE_STDIO_H */
488 SDL_RWops
*SDL_RWFromMem(void *mem
, int size
)
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
;
505 SDL_RWops
*SDL_RWFromConstMem(const void *mem
, int size
)
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
;
522 SDL_RWops
*SDL_AllocRW(void)
526 area
= (SDL_RWops
*)SDL_malloc(sizeof *area
);
527 if ( area
== NULL
) {
533 void SDL_FreeRW(SDL_RWops
*area
)
538 /* Functions for dynamically reading and writing endian-specific values */
540 Uint16
SDL_ReadLE16 (SDL_RWops
*src
)
544 SDL_RWread(src
, &value
, (sizeof value
), 1);
545 return(SDL_SwapLE16(value
));
547 Uint16
SDL_ReadBE16 (SDL_RWops
*src
)
551 SDL_RWread(src
, &value
, (sizeof value
), 1);
552 return(SDL_SwapBE16(value
));
554 Uint32
SDL_ReadLE32 (SDL_RWops
*src
)
558 SDL_RWread(src
, &value
, (sizeof value
), 1);
559 return(SDL_SwapLE32(value
));
561 Uint32
SDL_ReadBE32 (SDL_RWops
*src
)
565 SDL_RWread(src
, &value
, (sizeof value
), 1);
566 return(SDL_SwapBE32(value
));
568 Uint64
SDL_ReadLE64 (SDL_RWops
*src
)
572 SDL_RWread(src
, &value
, (sizeof value
), 1);
573 return(SDL_SwapLE64(value
));
575 Uint64
SDL_ReadBE64 (SDL_RWops
*src
)
579 SDL_RWread(src
, &value
, (sizeof value
), 1);
580 return(SDL_SwapBE64(value
));
583 int SDL_WriteLE16 (SDL_RWops
*dst
, Uint16 value
)
585 value
= SDL_SwapLE16(value
);
586 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));
588 int SDL_WriteBE16 (SDL_RWops
*dst
, Uint16 value
)
590 value
= SDL_SwapBE16(value
);
591 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));
593 int SDL_WriteLE32 (SDL_RWops
*dst
, Uint32 value
)
595 value
= SDL_SwapLE32(value
);
596 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));
598 int SDL_WriteBE32 (SDL_RWops
*dst
, Uint32 value
)
600 value
= SDL_SwapBE32(value
);
601 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));
603 int SDL_WriteLE64 (SDL_RWops
*dst
, Uint64 value
)
605 value
= SDL_SwapLE64(value
);
606 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));
608 int SDL_WriteBE64 (SDL_RWops
*dst
, Uint64 value
)
610 value
= SDL_SwapBE64(value
);
611 return(SDL_RWwrite(dst
, &value
, (sizeof value
), 1));