add virtual baseclass for data sources (iDataSource)
[enigma2.git] / lib / base / rawfile.cpp
1 #include <cstdio>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <lib/base/rawfile.h>
5 #include <lib/base/eerror.h>
6
7 DEFINE_REF(eRawFile);
8
9 eRawFile::eRawFile()
10         :m_lock(true)
11 {
12         m_fd = -1;
13         m_file = 0;
14         m_splitsize = 0;
15         m_totallength = 0;
16         m_current_offset = 0;
17         m_base_offset = 0;
18         m_last_offset = 0;
19         m_nrfiles = 0;
20         m_current_file = 0;
21 }
22
23 eRawFile::~eRawFile()
24 {
25         close();
26 }
27
28 int eRawFile::open(const char *filename, int cached)
29 {
30         close();
31         m_cached = cached;
32         m_basename = filename;
33         scan();
34         m_current_offset = 0;
35         m_last_offset = 0;
36         if (!m_cached)
37         {
38                 m_fd = ::open(filename, O_RDONLY | O_LARGEFILE);
39                 return m_fd;
40         } else
41         {
42                 m_file = ::fopen64(filename, "rb");
43                 if (!m_file)
44                         return -1;
45                 return 0;
46         }
47 }
48
49 void eRawFile::setfd(int fd)
50 {
51         close();
52         m_cached = 0;
53         m_nrfiles = 1;
54         m_fd = fd;
55 }
56
57 off_t eRawFile::lseek(off_t offset, int whence)
58 {
59 //      eDebug("lseek: %lld, %d", offset, whence);
60                 /* if there is only one file, use the native lseek - the file could be growing! */
61         if (m_nrfiles < 2)
62         {
63                 if (!m_cached)
64                         return ::lseek(m_fd, offset, whence);
65                 else
66                 {
67                         ::fseeko(m_file, offset, whence);
68                         return ::ftello(m_file);
69                 }
70         }
71         switch (whence)
72         {
73         case SEEK_SET:
74                 m_current_offset = offset;
75                 break;
76         case SEEK_CUR:
77                 m_current_offset += offset;
78                 break;
79         case SEEK_END:
80                 m_current_offset = m_totallength + offset;
81                 break;
82         }
83
84         if (m_current_offset < 0)
85                 m_current_offset = 0;
86         return m_current_offset;
87 }
88
89 int eRawFile::close()
90 {
91         if (m_cached)
92         {
93                 if (!m_file)
94                         return -1;
95                 ::fclose(m_file);
96                 m_file = 0;
97                 return 0;
98         } else
99         {
100                 int ret = ::close(m_fd);
101                 m_fd = -1;
102                 return ret;
103         }
104 }
105
106 ssize_t eRawFile::read(void *buf, size_t count)
107 {
108 //      eDebug("read: %p, %d", buf, count);
109         switchOffset(m_current_offset);
110         
111         if (m_nrfiles >= 2)
112         {
113                 if (m_current_offset + count > m_totallength)
114                         count = m_totallength - m_current_offset;
115                 if (count < 0)
116                         return 0;
117         }
118         
119         int ret;
120         
121         if (!m_cached)
122                 ret = ::read(m_fd, buf, count);
123         else
124                 ret = ::fread(buf, 1, count, m_file);
125
126         if (ret > 0)
127                 m_current_offset = m_last_offset += ret;
128         return ret;
129 }
130
131 int eRawFile::valid()
132 {
133         if (!m_cached)
134                 return m_fd != -1;
135         else
136                 return !!m_file;
137 }
138
139 void eRawFile::scan()
140 {
141         m_nrfiles = 0;
142         m_totallength = 0;
143         while (m_nrfiles < 1000) /* .999 is the last possible */
144         {
145                 if (!m_cached)
146                 {
147                         int f = openFileUncached(m_nrfiles);
148                         if (f < 0)
149                                 break;
150                         if (!m_nrfiles)
151                                 m_splitsize = ::lseek(f, 0, SEEK_END);
152                         m_totallength += ::lseek(f, 0, SEEK_END);
153                         ::close(f);
154                 } else
155                 {
156                         FILE *f = openFileCached(m_nrfiles);
157                         if (!f)
158                                 break;
159                         ::fseeko(f, 0, SEEK_END);
160                         if (!m_nrfiles)
161                                 m_splitsize = ::ftello(f);
162                         m_totallength += ::ftello(f);
163                         ::fclose(f);
164                 }
165                 
166                 ++m_nrfiles;
167         }
168 //      eDebug("found %d files, splitsize: %llx, totallength: %llx", m_nrfiles, m_splitsize, m_totallength);
169 }
170
171 int eRawFile::switchOffset(off_t off)
172 {
173         if (m_splitsize)
174         {
175                 int filenr = off / m_splitsize;
176                 if (filenr >= m_nrfiles)
177                         filenr = m_nrfiles - 1;
178                 if (filenr != m_current_file)
179                 {       
180 //                      eDebug("-> %d", filenr);
181                         close();
182                         if (!m_cached)
183                                 m_fd = openFileUncached(filenr);
184                         else
185                                 m_file = openFileCached(filenr);
186                         m_last_offset = m_base_offset = m_splitsize * filenr;
187                         m_current_file = filenr;
188                 }
189         } else
190                 m_base_offset = 0;
191         
192         if (off != m_last_offset)
193         {
194                 if (!m_cached)
195                         m_last_offset = ::lseek(m_fd, off - m_base_offset, SEEK_SET) + m_base_offset;
196                 else
197                 {
198                         ::fseeko(m_file, off - m_base_offset, SEEK_SET);
199                         m_last_offset = ::ftello(m_file) + m_base_offset;
200                 }
201                 return m_last_offset;
202         } else
203         {
204                 return m_last_offset;
205         }
206 }
207
208 /* m_cached */
209 FILE *eRawFile::openFileCached(int nr)
210 {
211         std::string filename = m_basename;
212         if (nr)
213         {
214                 char suffix[5];
215                 snprintf(suffix, 5, ".%03d", nr);
216                 filename += suffix;
217         }
218         return ::fopen64(filename.c_str(), "rb");
219 }
220
221 /* !m_cached */
222 int eRawFile::openFileUncached(int nr)
223 {
224         std::string filename = m_basename;
225         if (nr)
226         {
227                 char suffix[5];
228                 snprintf(suffix, 5, ".%03d", nr);
229                 filename += suffix;
230         }
231         return ::open(filename.c_str(), O_RDONLY | O_LARGEFILE);
232 }
233
234 off_t eRawFile::length()
235 {
236         return m_totallength;
237 }
238
239 off_t eRawFile::position()
240 {
241         if (m_nrfiles < 2)
242         {
243                 if (!m_cached)
244                         return ::lseek(m_fd, 0, SEEK_CUR);
245                 else
246                         return ::fseeko(m_file, 0, SEEK_CUR);
247         }
248         return m_current_offset;
249 }
250
251 eSingleLock &eRawFile::getLock()
252 {
253         return m_lock;
254 }