1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "SDL_config.h"
22
23 /* This file provides a general interface for SDL to read and write
24 data sources. It can easily be extended to files, memory, etc.
25 */
26
27 #include "SDL_endian.h"
28 #include "SDL_rwops.h"
29
30 #ifdef __APPLE__
31 #include "cocoa/SDL_rwopsbundlesupport.h"
32 #endif /* __APPLE__ */
33
34 #ifdef ANDROID
35 #include "../core/android/SDL_android.h"
36 #include <android/log.h>
37 #endif
38
39 #ifdef __NDS__
40 /* include libfat headers for fatInitDefault(). */
41 #include <fat.h>
42 #endif /* __NDS__ */
43
44 #ifdef __WIN32__
45
46 /* Functions to read/write Win32 API file pointers */
47 /* Will not use it on WinCE because stdio is buffered, it means
48 faster, and all stdio functions anyway are embedded in coredll.dll -
49 the main wince dll*/
50
51 #include "../core/windows/SDL_windows.h"
52
53 #ifndef INVALID_SET_FILE_POINTER
54 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
55 #endif
56
57 #define READAHEAD_BUFFER_SIZE 1024
58
59 static int SDLCALL
windows_file_open(SDL_RWops * context,const char * filename,const char * mode)60 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
61 {
62 #ifndef _WIN32_WCE
63 UINT old_error_mode;
64 #endif
65 HANDLE h;
66 DWORD r_right, w_right;
67 DWORD must_exist, truncate;
68 int a_mode;
69
70 if (!context)
71 return -1; /* failed (invalid call) */
72
73 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */
74 context->hidden.windowsio.buffer.data = NULL;
75 context->hidden.windowsio.buffer.size = 0;
76 context->hidden.windowsio.buffer.left = 0;
77
78 /* "r" = reading, file must exist */
79 /* "w" = writing, truncate existing, file may not exist */
80 /* "r+"= reading or writing, file must exist */
81 /* "a" = writing, append file may not exist */
82 /* "a+"= append + read, file may not exist */
83 /* "w+" = read, write, truncate. file may not exist */
84
85 must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
86 truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
87 r_right = (SDL_strchr(mode, '+') != NULL
88 || must_exist) ? GENERIC_READ : 0;
89 a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
90 w_right = (a_mode || SDL_strchr(mode, '+')
91 || truncate) ? GENERIC_WRITE : 0;
92
93 if (!r_right && !w_right) /* inconsistent mode */
94 return -1; /* failed (invalid call) */
95
96 context->hidden.windowsio.buffer.data =
97 (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
98 if (!context->hidden.windowsio.buffer.data) {
99 SDL_OutOfMemory();
100 return -1;
101 }
102 #ifdef _WIN32_WCE
103 {
104 LPTSTR tstr = WIN_UTF8ToString(filename);
105 h = CreateFile(tstr, (w_right | r_right),
106 (w_right) ? 0 : FILE_SHARE_READ, NULL,
107 (must_exist | truncate | a_mode),
108 FILE_ATTRIBUTE_NORMAL, NULL);
109 SDL_free(tstr);
110 }
111 #else
112 /* Do not open a dialog box if failure */
113 old_error_mode =
114 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
115
116 {
117 LPTSTR tstr = WIN_UTF8ToString(filename);
118 h = CreateFile(tstr, (w_right | r_right),
119 (w_right) ? 0 : FILE_SHARE_READ, NULL,
120 (must_exist | truncate | a_mode),
121 FILE_ATTRIBUTE_NORMAL, NULL);
122 SDL_free(tstr);
123 }
124
125 /* restore old behavior */
126 SetErrorMode(old_error_mode);
127 #endif /* _WIN32_WCE */
128
129 if (h == INVALID_HANDLE_VALUE) {
130 SDL_free(context->hidden.windowsio.buffer.data);
131 context->hidden.windowsio.buffer.data = NULL;
132 SDL_SetError("Couldn't open %s", filename);
133 return -2; /* failed (CreateFile) */
134 }
135 context->hidden.windowsio.h = h;
136 context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
137
138 return 0; /* ok */
139 }
140
141 static long SDLCALL
windows_file_seek(SDL_RWops * context,long offset,int whence)142 windows_file_seek(SDL_RWops * context, long offset, int whence)
143 {
144 DWORD windowswhence;
145 long file_pos;
146
147 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
148 SDL_SetError("windows_file_seek: invalid context/file not opened");
149 return -1;
150 }
151
152 /* FIXME: We may be able to satisfy the seek within buffered data */
153 if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
154 offset -= (long)context->hidden.windowsio.buffer.left;
155 }
156 context->hidden.windowsio.buffer.left = 0;
157
158 switch (whence) {
159 case RW_SEEK_SET:
160 windowswhence = FILE_BEGIN;
161 break;
162 case RW_SEEK_CUR:
163 windowswhence = FILE_CURRENT;
164 break;
165 case RW_SEEK_END:
166 windowswhence = FILE_END;
167 break;
168 default:
169 SDL_SetError("windows_file_seek: Unknown value for 'whence'");
170 return -1;
171 }
172
173 file_pos =
174 SetFilePointer(context->hidden.windowsio.h, offset, NULL, windowswhence);
175
176 if (file_pos != INVALID_SET_FILE_POINTER)
177 return file_pos; /* success */
178
179 SDL_Error(SDL_EFSEEK);
180 return -1; /* error */
181 }
182
183 static size_t SDLCALL
windows_file_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)184 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
185 {
186 size_t total_need;
187 size_t total_read = 0;
188 size_t read_ahead;
189 DWORD byte_read;
190
191 total_need = size * maxnum;
192
193 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
194 || !total_need)
195 return 0;
196
197 if (context->hidden.windowsio.buffer.left > 0) {
198 void *data = (char *) context->hidden.windowsio.buffer.data +
199 context->hidden.windowsio.buffer.size -
200 context->hidden.windowsio.buffer.left;
201 read_ahead =
202 SDL_min(total_need, context->hidden.windowsio.buffer.left);
203 SDL_memcpy(ptr, data, read_ahead);
204 context->hidden.windowsio.buffer.left -= read_ahead;
205
206 if (read_ahead == total_need) {
207 return maxnum;
208 }
209 ptr = (char *) ptr + read_ahead;
210 total_need -= read_ahead;
211 total_read += read_ahead;
212 }
213
214 if (total_need < READAHEAD_BUFFER_SIZE) {
215 if (!ReadFile
216 (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
217 READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
218 SDL_Error(SDL_EFREAD);
219 return 0;
220 }
221 read_ahead = SDL_min(total_need, (int) byte_read);
222 SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
223 context->hidden.windowsio.buffer.size = byte_read;
224 context->hidden.windowsio.buffer.left = byte_read - read_ahead;
225 total_read += read_ahead;
226 } else {
227 if (!ReadFile
228 (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
229 SDL_Error(SDL_EFREAD);
230 return 0;
231 }
232 total_read += byte_read;
233 }
234 return (total_read / size);
235 }
236
237 static size_t SDLCALL
windows_file_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)238 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
239 size_t num)
240 {
241
242 size_t total_bytes;
243 DWORD byte_written;
244 size_t nwritten;
245
246 total_bytes = size * num;
247
248 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
249 || total_bytes <= 0 || !size)
250 return 0;
251
252 if (context->hidden.windowsio.buffer.left) {
253 SetFilePointer(context->hidden.windowsio.h,
254 -(LONG)context->hidden.windowsio.buffer.left, NULL,
255 FILE_CURRENT);
256 context->hidden.windowsio.buffer.left = 0;
257 }
258
259 /* if in append mode, we must go to the EOF before write */
260 if (context->hidden.windowsio.append) {
261 if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
262 INVALID_SET_FILE_POINTER) {
263 SDL_Error(SDL_EFWRITE);
264 return 0;
265 }
266 }
267
268 if (!WriteFile
269 (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
270 SDL_Error(SDL_EFWRITE);
271 return 0;
272 }
273
274 nwritten = byte_written / size;
275 return nwritten;
276 }
277
278 static int SDLCALL
windows_file_close(SDL_RWops * context)279 windows_file_close(SDL_RWops * context)
280 {
281
282 if (context) {
283 if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
284 CloseHandle(context->hidden.windowsio.h);
285 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */
286 }
287 if (context->hidden.windowsio.buffer.data) {
288 SDL_free(context->hidden.windowsio.buffer.data);
289 context->hidden.windowsio.buffer.data = NULL;
290 }
291 SDL_FreeRW(context);
292 }
293 return (0);
294 }
295 #endif /* __WIN32__ */
296
297 #ifdef HAVE_STDIO_H
298
299 /* Functions to read/write stdio file pointers */
300
301 static long SDLCALL
stdio_seek(SDL_RWops * context,long offset,int whence)302 stdio_seek(SDL_RWops * context, long offset, int whence)
303 {
304 if (fseek(context->hidden.stdio.fp, offset, whence) == 0) {
305 return (ftell(context->hidden.stdio.fp));
306 } else {
307 SDL_Error(SDL_EFSEEK);
308 return (-1);
309 }
310 }
311
312 static size_t SDLCALL
stdio_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)313 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
314 {
315 size_t nread;
316
317 nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
318 if (nread == 0 && ferror(context->hidden.stdio.fp)) {
319 SDL_Error(SDL_EFREAD);
320 }
321 return (nread);
322 }
323
324 static size_t SDLCALL
stdio_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)325 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
326 {
327 size_t nwrote;
328
329 nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
330 if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
331 SDL_Error(SDL_EFWRITE);
332 }
333 return (nwrote);
334 }
335
336 static int SDLCALL
stdio_close(SDL_RWops * context)337 stdio_close(SDL_RWops * context)
338 {
339 int status = 0;
340 if (context) {
341 if (context->hidden.stdio.autoclose) {
342 /* WARNING: Check the return value here! */
343 if (fclose(context->hidden.stdio.fp) != 0) {
344 SDL_Error(SDL_EFWRITE);
345 status = -1;
346 }
347 }
348 SDL_FreeRW(context);
349 }
350 return status;
351 }
352 #endif /* !HAVE_STDIO_H */
353
354 /* Functions to read/write memory pointers */
355
356 static long SDLCALL
mem_seek(SDL_RWops * context,long offset,int whence)357 mem_seek(SDL_RWops * context, long offset, int whence)
358 {
359 Uint8 *newpos;
360
361 switch (whence) {
362 case RW_SEEK_SET:
363 newpos = context->hidden.mem.base + offset;
364 break;
365 case RW_SEEK_CUR:
366 newpos = context->hidden.mem.here + offset;
367 break;
368 case RW_SEEK_END:
369 newpos = context->hidden.mem.stop + offset;
370 break;
371 default:
372 SDL_SetError("Unknown value for 'whence'");
373 return (-1);
374 }
375 if (newpos < context->hidden.mem.base) {
376 newpos = context->hidden.mem.base;
377 }
378 if (newpos > context->hidden.mem.stop) {
379 newpos = context->hidden.mem.stop;
380 }
381 context->hidden.mem.here = newpos;
382 return (long)(context->hidden.mem.here - context->hidden.mem.base);
383 }
384
385 static size_t SDLCALL
mem_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)386 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
387 {
388 size_t total_bytes;
389 size_t mem_available;
390
391 total_bytes = (maxnum * size);
392 if ((maxnum <= 0) || (size <= 0)
393 || ((total_bytes / maxnum) != (size_t) size)) {
394 return 0;
395 }
396
397 mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
398 if (total_bytes > mem_available) {
399 total_bytes = mem_available;
400 }
401
402 SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
403 context->hidden.mem.here += total_bytes;
404
405 return (total_bytes / size);
406 }
407
408 static size_t SDLCALL
mem_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)409 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
410 {
411 if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
412 num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
413 }
414 SDL_memcpy(context->hidden.mem.here, ptr, num * size);
415 context->hidden.mem.here += num * size;
416 return (num);
417 }
418
419 static size_t SDLCALL
mem_writeconst(SDL_RWops * context,const void * ptr,size_t size,size_t num)420 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
421 {
422 SDL_SetError("Can't write to read-only memory");
423 return (-1);
424 }
425
426 static int SDLCALL
mem_close(SDL_RWops * context)427 mem_close(SDL_RWops * context)
428 {
429 if (context) {
430 SDL_FreeRW(context);
431 }
432 return (0);
433 }
434
435
436 /* Functions to create SDL_RWops structures from various data sources */
437
438 SDL_RWops *
SDL_RWFromFile(const char * file,const char * mode)439 SDL_RWFromFile(const char *file, const char *mode)
440 {
441 SDL_RWops *rwops = NULL;
442 #ifdef HAVE_STDIO_H
443 FILE *fp = NULL;
444 #endif
445 if (!file || !*file || !mode || !*mode) {
446 SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
447 return NULL;
448 }
449 #if defined(ANDROIDXELI) //Xeli: dont use the android assets, TODO create fallback system
450 rwops = SDL_AllocRW();
451 if (!rwops)
452 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
453 if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
454 SDL_FreeRW(rwops);
455 return NULL;
456 }
457 rwops->seek = Android_JNI_FileSeek;
458 rwops->read = Android_JNI_FileRead;
459 rwops->write = Android_JNI_FileWrite;
460 rwops->close = Android_JNI_FileClose;
461
462 #elif defined(__WIN32__)
463 rwops = SDL_AllocRW();
464 if (!rwops)
465 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
466 if (windows_file_open(rwops, file, mode) < 0) {
467 SDL_FreeRW(rwops);
468 return NULL;
469 }
470 rwops->seek = windows_file_seek;
471 rwops->read = windows_file_read;
472 rwops->write = windows_file_write;
473 rwops->close = windows_file_close;
474
475 #elif HAVE_STDIO_H
476 #ifdef __APPLE__
477 fp = SDL_OpenFPFromBundleOrFallback(file, mode);
478 #else
479 fp = fopen(file, mode);
480 #endif
481 if (fp == NULL) {
482 SDL_SetError("Couldn't open %s", file);
483 } else {
484 rwops = SDL_RWFromFP(fp, 1);
485 }
486 #else
487 SDL_SetError("SDL not compiled with stdio support");
488 #endif /* !HAVE_STDIO_H */
489
490 return (rwops);
491 }
492
493 #ifdef HAVE_STDIO_H
494 SDL_RWops *
SDL_RWFromFP(FILE * fp,SDL_bool autoclose)495 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
496 {
497 SDL_RWops *rwops = NULL;
498
499 #if 0
500 /*#ifdef __NDS__*/
501 /* set it up so we can use stdio file function */
502 fatInitDefault();
503 printf("called fatInitDefault()");
504 #endif /* __NDS__ */
505
506 rwops = SDL_AllocRW();
507 if (rwops != NULL) {
508 rwops->seek = stdio_seek;
509 rwops->read = stdio_read;
510 rwops->write = stdio_write;
511 rwops->close = stdio_close;
512 rwops->hidden.stdio.fp = fp;
513 rwops->hidden.stdio.autoclose = autoclose;
514 }
515 return (rwops);
516 }
517 #else
518 SDL_RWops *
SDL_RWFromFP(void * fp,SDL_bool autoclose)519 SDL_RWFromFP(void * fp, SDL_bool autoclose)
520 {
521 SDL_SetError("SDL not compiled with stdio support");
522 return NULL;
523 }
524 #endif /* HAVE_STDIO_H */
525
526 SDL_RWops *
SDL_RWFromMem(void * mem,int size)527 SDL_RWFromMem(void *mem, int size)
528 {
529 SDL_RWops *rwops;
530
531 rwops = SDL_AllocRW();
532 if (rwops != NULL) {
533 rwops->seek = mem_seek;
534 rwops->read = mem_read;
535 rwops->write = mem_write;
536 rwops->close = mem_close;
537 rwops->hidden.mem.base = (Uint8 *) mem;
538 rwops->hidden.mem.here = rwops->hidden.mem.base;
539 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
540 }
541 return (rwops);
542 }
543
544 SDL_RWops *
SDL_RWFromConstMem(const void * mem,int size)545 SDL_RWFromConstMem(const void *mem, int size)
546 {
547 SDL_RWops *rwops;
548
549 rwops = SDL_AllocRW();
550 if (rwops != NULL) {
551 rwops->seek = mem_seek;
552 rwops->read = mem_read;
553 rwops->write = mem_writeconst;
554 rwops->close = mem_close;
555 rwops->hidden.mem.base = (Uint8 *) mem;
556 rwops->hidden.mem.here = rwops->hidden.mem.base;
557 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
558 }
559 return (rwops);
560 }
561
562 SDL_RWops *
SDL_AllocRW(void)563 SDL_AllocRW(void)
564 {
565 SDL_RWops *area;
566
567 area = (SDL_RWops *) SDL_malloc(sizeof *area);
568 if (area == NULL) {
569 SDL_OutOfMemory();
570 }
571 return (area);
572 }
573
574 void
SDL_FreeRW(SDL_RWops * area)575 SDL_FreeRW(SDL_RWops * area)
576 {
577 SDL_free(area);
578 }
579
580 /* Functions for dynamically reading and writing endian-specific values */
581
582 Uint16
SDL_ReadLE16(SDL_RWops * src)583 SDL_ReadLE16(SDL_RWops * src)
584 {
585 Uint16 value;
586
587 SDL_RWread(src, &value, (sizeof value), 1);
588 return (SDL_SwapLE16(value));
589 }
590
591 Uint16
SDL_ReadBE16(SDL_RWops * src)592 SDL_ReadBE16(SDL_RWops * src)
593 {
594 Uint16 value;
595
596 SDL_RWread(src, &value, (sizeof value), 1);
597 return (SDL_SwapBE16(value));
598 }
599
600 Uint32
SDL_ReadLE32(SDL_RWops * src)601 SDL_ReadLE32(SDL_RWops * src)
602 {
603 Uint32 value;
604
605 SDL_RWread(src, &value, (sizeof value), 1);
606 return (SDL_SwapLE32(value));
607 }
608
609 Uint32
SDL_ReadBE32(SDL_RWops * src)610 SDL_ReadBE32(SDL_RWops * src)
611 {
612 Uint32 value;
613
614 SDL_RWread(src, &value, (sizeof value), 1);
615 return (SDL_SwapBE32(value));
616 }
617
618 Uint64
SDL_ReadLE64(SDL_RWops * src)619 SDL_ReadLE64(SDL_RWops * src)
620 {
621 Uint64 value;
622
623 SDL_RWread(src, &value, (sizeof value), 1);
624 return (SDL_SwapLE64(value));
625 }
626
627 Uint64
SDL_ReadBE64(SDL_RWops * src)628 SDL_ReadBE64(SDL_RWops * src)
629 {
630 Uint64 value;
631
632 SDL_RWread(src, &value, (sizeof value), 1);
633 return (SDL_SwapBE64(value));
634 }
635
636 size_t
SDL_WriteLE16(SDL_RWops * dst,Uint16 value)637 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
638 {
639 value = SDL_SwapLE16(value);
640 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
641 }
642
643 size_t
SDL_WriteBE16(SDL_RWops * dst,Uint16 value)644 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
645 {
646 value = SDL_SwapBE16(value);
647 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
648 }
649
650 size_t
SDL_WriteLE32(SDL_RWops * dst,Uint32 value)651 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
652 {
653 value = SDL_SwapLE32(value);
654 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
655 }
656
657 size_t
SDL_WriteBE32(SDL_RWops * dst,Uint32 value)658 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
659 {
660 value = SDL_SwapBE32(value);
661 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
662 }
663
664 size_t
SDL_WriteLE64(SDL_RWops * dst,Uint64 value)665 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
666 {
667 value = SDL_SwapLE64(value);
668 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
669 }
670
671 size_t
SDL_WriteBE64(SDL_RWops * dst,Uint64 value)672 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
673 {
674 value = SDL_SwapBE64(value);
675 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
676 }
677
678 /* vi: set ts=4 sw=4 expandtab: */
679