Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / src / physfs / physfs_stream.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "physfs/physfs_stream.hpp"
18
19 #include <config.h>
20
21 #include <assert.h>
22 #include <sstream>
23 #include <stdexcept>
24
25 IFileStreambuf::IFileStreambuf(const std::string& filename)
26 {
27   // check this as PHYSFS seems to be buggy and still returns a
28   // valid pointer in this case
29   if(filename == "") {
30     throw std::runtime_error("Couldn't open file: empty filename");
31   }
32   file = PHYSFS_openRead(filename.c_str());
33   if(file == 0) {
34     std::stringstream msg;
35     msg << "Couldn't open file '" << filename << "': "
36         << PHYSFS_getLastError();
37     throw std::runtime_error(msg.str());
38   }
39 }
40
41 IFileStreambuf::~IFileStreambuf()
42 {
43   PHYSFS_close(file);
44 }
45
46 int
47 IFileStreambuf::underflow()
48 {
49   if(PHYSFS_eof(file)) {
50     return traits_type::eof();
51   }
52
53   PHYSFS_sint64 bytesread = PHYSFS_read(file, buf, 1, sizeof(buf));
54   if(bytesread <= 0) {
55     return traits_type::eof();
56   }
57   setg(buf, buf, buf + bytesread);
58
59   return buf[0];
60 }
61
62 IFileStreambuf::pos_type
63 IFileStreambuf::seekpos(pos_type pos, std::ios_base::openmode)
64 {
65   if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (pos)) == 0) {
66     return pos_type(off_type(-1));
67   }
68
69   // the seek invalidated the buffer
70   setg(buf, buf, buf);
71   return pos;
72 }
73
74 IFileStreambuf::pos_type
75 IFileStreambuf::seekoff(off_type off, std::ios_base::seekdir dir,
76                         std::ios_base::openmode mode)
77 {
78   off_type pos = off;
79   PHYSFS_sint64 ptell = PHYSFS_tell(file);
80
81   switch(dir) {
82     case std::ios_base::beg:
83       break;
84     case std::ios_base::cur:
85       if(off == 0)
86         return static_cast<pos_type> (ptell) - static_cast<pos_type> (egptr() - gptr());
87       pos += static_cast<off_type> (ptell) - static_cast<off_type> (egptr() - gptr());
88       break;
89     case std::ios_base::end:
90       pos += static_cast<off_type> (PHYSFS_fileLength(file));
91       break;
92     default:
93 #ifdef DEBUG
94       assert(false);
95 #else
96       return pos_type(off_type(-1));
97 #endif
98   }
99
100   return seekpos(static_cast<pos_type> (pos), mode);
101 }
102
103 //---------------------------------------------------------------------------
104
105 OFileStreambuf::OFileStreambuf(const std::string& filename)
106 {
107   file = PHYSFS_openWrite(filename.c_str());
108   if(file == 0) {
109     std::stringstream msg;
110     msg << "Couldn't open file '" << filename << "': "
111         << PHYSFS_getLastError();
112     throw std::runtime_error(msg.str());
113   }
114
115   setp(buf, buf+sizeof(buf));
116 }
117
118 OFileStreambuf::~OFileStreambuf()
119 {
120   sync();
121   PHYSFS_close(file);
122 }
123
124 int
125 OFileStreambuf::overflow(int c)
126 {
127   char c2 = (char)c;
128
129   if(pbase() == pptr())
130     return 0;
131
132   size_t size = pptr() - pbase();
133   PHYSFS_sint64 res = PHYSFS_write(file, pbase(), 1, size);
134   if(res <= 0)
135     return traits_type::eof();
136
137   if(c != traits_type::eof()) {
138     PHYSFS_sint64 res = PHYSFS_write(file, &c2, 1, 1);
139     if(res <= 0)
140       return traits_type::eof();
141   }
142
143   setp(buf, buf + res);
144   return 0;
145 }
146
147 int
148 OFileStreambuf::sync()
149 {
150   return overflow(traits_type::eof());
151 }
152
153 //---------------------------------------------------------------------------
154
155 IFileStream::IFileStream(const std::string& filename)
156   : std::istream(new IFileStreambuf(filename))
157 {
158 }
159
160 IFileStream::~IFileStream()
161 {
162   delete rdbuf();
163 }
164
165 //---------------------------------------------------------------------------
166
167 OFileStream::OFileStream(const std::string& filename)
168   : std::ostream(new OFileStreambuf(filename))
169 {
170 }
171
172 OFileStream::~OFileStream()
173 {
174   delete rdbuf();
175 }
176
177 /* EOF */