1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009 by Chris Robinson.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 /* Title: File I/O */
25 
26 #include "config.h"
27 
28 #include "main.h"
29 
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 
36 #include <iostream>
37 
38 
underflow()39 MemStreamBuf::int_type MemStreamBuf::underflow()
40 {
41     if(gptr() == egptr())
42     {
43         char_type *data = (char_type*)memInfo.Data;
44         setg(data, data + memInfo.Pos, data + memInfo.Length);
45         memInfo.Pos = memInfo.Length;
46     }
47     if(gptr() == egptr())
48         return traits_type::eof();
49     return (*gptr())&0xFF;
50 }
51 
seekoff(off_type offset,std::ios_base::seekdir whence,std::ios_base::openmode mode)52 MemStreamBuf::pos_type MemStreamBuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode)
53 {
54     if((mode&std::ios_base::out))
55         return traits_type::eof();
56 
57     pos_type pos;
58     switch(whence)
59     {
60         case std::ios_base::beg:
61             pos = pos_type(offset);
62             break;
63         case std::ios_base::cur:
64             pos = pos_type(memInfo.Pos) - pos_type(egptr()-gptr());
65             pos += offset;
66             break;
67         case std::ios_base::end:
68             pos = memInfo.Length + pos_type(offset);
69             break;
70         default:
71             return traits_type::eof();
72     }
73 
74     return seekpos(pos, mode);
75 }
76 
seekpos(pos_type pos,std::ios_base::openmode mode)77 MemStreamBuf::pos_type MemStreamBuf::seekpos(pos_type pos, std::ios_base::openmode mode)
78 {
79     if((mode&std::ios_base::out))
80         return traits_type::eof();
81 
82     if(pos < 0 || pos > pos_type(memInfo.Length) || pos != pos_type(size_t(pos)))
83         return traits_type::eof();
84     memInfo.Pos = pos;
85 
86     setg(0, 0, 0);
87     return pos;
88 }
89 
90 
underflow()91 int FileStreamBuf::underflow()
92 {
93     if(usrFile && gptr() == egptr())
94     {
95         ALsizei amt = fio.read(usrFile, reinterpret_cast<ALubyte*>(&buffer[0]), sizeof(buffer));
96         if(amt >= 0) setg(buffer, buffer, buffer+amt);
97     }
98     if(gptr() == egptr())
99         return traits_type::eof();
100     return (*gptr())&0xFF;
101 }
102 
seekoff(off_type offset,std::ios_base::seekdir whence,std::ios_base::openmode mode)103 FileStreamBuf::pos_type FileStreamBuf::seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode)
104 {
105     if(!usrFile || (mode&std::ios_base::out))
106         return traits_type::eof();
107 
108     pos_type pos = traits_type::eof();
109     switch(whence)
110     {
111         case std::ios_base::beg:
112             if(offset == off_type(alureInt64(offset)))
113                 pos = pos_type(fio.seek(usrFile, offset, SEEK_SET));
114             break;
115 
116         case std::ios_base::cur:
117             offset -= off_type(egptr()-gptr());
118             if(offset == off_type(alureInt64(offset)))
119                 pos = pos_type(fio.seek(usrFile, offset, SEEK_CUR));
120             break;
121 
122         case std::ios_base::end:
123             if(offset == off_type(alureInt64(offset)))
124                 pos = pos_type(fio.seek(usrFile, offset, SEEK_END));
125             break;
126 
127         default:
128             break;
129     }
130     if(pos >= 0)
131         setg(0, 0, 0);
132     return pos;
133 }
134 
seekpos(pos_type pos,std::ios_base::openmode mode)135 FileStreamBuf::pos_type FileStreamBuf::seekpos(pos_type pos, std::ios_base::openmode mode)
136 {
137     if(pos != pos_type(off_type(pos)))
138         return traits_type::eof();
139     return seekoff(off_type(pos), std::ios_base::beg, mode);
140 }
141 
142 
open_wrap(const char * filename,ALuint mode)143 static void *open_wrap(const char *filename, ALuint mode)
144 {
145     if(mode != 0)
146         return NULL;
147 
148     return fopen(filename, "rb");
149 }
150 
close_wrap(void * user_data)151 static void close_wrap(void *user_data)
152 {
153     FILE *f = (FILE*)user_data;
154     fclose(f);
155 }
156 
read_wrap(void * user_data,ALubyte * buf,ALuint bytes)157 static ALsizei read_wrap(void *user_data, ALubyte *buf, ALuint bytes)
158 {
159     FILE *f = (FILE*)user_data;
160     return fread(buf, 1, bytes, f);
161 }
162 
write_wrap(void * user_data,const ALubyte * buf,ALuint bytes)163 static ALsizei write_wrap(void *user_data, const ALubyte *buf, ALuint bytes)
164 {
165     FILE *f = (FILE*)user_data;
166     return fwrite(buf, 1, bytes, f);
167 }
168 
seek_wrap(void * user_data,alureInt64 offset,int whence)169 static alureInt64 seek_wrap(void *user_data, alureInt64 offset, int whence)
170 {
171     FILE *f = (FILE*)user_data;
172 #ifdef HAVE_FSEEKO
173     if(offset != (off_t)offset || fseeko(f, offset, whence) != 0)
174         return -1;
175     return ftello(f);
176 #elif defined(HAVE__FSEEKI64)
177     if(_fseeki64(f, offset, whence) != 0)
178         return -1;
179     return _ftelli64(f);
180 #else
181     if(offset != (long)offset || fseek(f, offset, whence) != 0)
182         return -1;
183     return ftell(f);
184 #endif
185 }
186 
187 UserFuncs Funcs = {
188     open_wrap,
189     close_wrap,
190     read_wrap,
191     write_wrap,
192     seek_wrap
193 };
194 bool UsingSTDIO = true;
195 
196 extern "C" {
197 
198 /* Function: alureSetIOCallbacks
199  *
200  * Provides callbacks for alternative methods to handle file I/O. Passing NULL
201  * for all callbacks is a valid way to revert to normal I/O, otherwise they
202  * must all be specified. Changing the callbacks will not affect open files
203  * (they will continue using the callbacks that were set at the time they were
204  * opened).
205  *
206  * Parameters:
207  * open - This callback is called to open the named file. The given mode is the
208  *        access rights the open file should have. Currently, this will always
209  *        be 0 for read-only (applications should check this to make sure, as
210  *        future versions may pass other values for other modes). Upon success,
211  *        a non-NULL handle must be returned which will be used as a unique
212  *        identifier for the file.
213  * close - This callback is called to close an opened file handle. The handle
214  *         will no longer be used after this function.
215  * read - This callback is called when data needs to be read from the given
216  *        handle. Up to the given number of bytes should be copied into 'buf'
217  *        and the number of bytes actually copied should be returned. Returning
218  *        0 means the end of the file has been reached (so non-blocking I/O
219  *        methods should ensure at least 1 byte gets read), and negative
220  *        indicates an error.
221  * write - This callback is called when data needs to be written to the given
222  *         handle. Up to the given number of bytes should be copied from 'buf'
223  *         and the number of bytes actually copied should be returned. A return
224  *         value of 0 means no more data can be written (so non-blocking I/O
225  *         methods should ensure at least 1 byte gets written), and negative
226  *         indicates an error.
227  * seek - This callback is called to reposition the offset of the file handle.
228  *        The given offset is interpreted according to 'whence', which may be
229  *        SEEK_SET (absolute position from the start of the file), SEEK_CUR
230  *        (relative position from the current offset), or SEEK_END (absolute
231  *        position from the end of the file), as defined by standard C. The new
232  *        offset from the beginning of the file should be returned. If the file
233  *        cannot seek, such as when using a FIFO, -1 should be returned.
234  *
235  * Returns:
236  * AL_FALSE on error.
237  *
238  * *Version Added*: 1.1
239  */
alureSetIOCallbacks(void * (* open)(const char * filename,ALuint mode),void (* close)(void * handle),ALsizei (* read)(void * handle,ALubyte * buf,ALuint bytes),ALsizei (* write)(void * handle,const ALubyte * buf,ALuint bytes),alureInt64 (* seek)(void * handle,alureInt64 offset,int whence))240 ALURE_API ALboolean ALURE_APIENTRY alureSetIOCallbacks(
241       void* (*open)(const char *filename, ALuint mode),
242       void (*close)(void *handle),
243       ALsizei (*read)(void *handle, ALubyte *buf, ALuint bytes),
244       ALsizei (*write)(void *handle, const ALubyte *buf, ALuint bytes),
245       alureInt64 (*seek)(void *handle, alureInt64 offset, int whence))
246 {
247     if(open && close && read && write && seek)
248     {
249         Funcs.open = open;
250         Funcs.close = close;
251         Funcs.read = read;
252         Funcs.write = write;
253         Funcs.seek = seek;
254         UsingSTDIO = false;
255         return AL_TRUE;
256     }
257 
258     if(!open && !close && !read && !write && !seek)
259     {
260         Funcs.open = open_wrap;
261         Funcs.close = close_wrap;
262         Funcs.read = read_wrap;
263         Funcs.write = write_wrap;
264         Funcs.seek = seek_wrap;
265         UsingSTDIO = true;
266         return AL_TRUE;
267     }
268 
269     SetError("Missing callback functions");
270     return AL_FALSE;
271 }
272 
273 } // extern "C"
274