Use _ftelli64 rather than ftell on windows.
[dcpomatic.git] / src / lib / file_group.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
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 2 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, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdio>
21 #include <sndfile.h>
22 #include "file_group.h"
23 #include "exceptions.h"
24 #include "cross.h"
25
26 using std::vector;
27 using std::cout;
28
29 FileGroup::FileGroup ()
30         : _current_path (0)
31         , _current_file (0)
32 {
33
34 }
35
36 FileGroup::FileGroup (boost::filesystem::path p)
37         : _current_path (0)
38         , _current_file (0)
39 {
40         _paths.push_back (p);
41         seek (0, SEEK_SET);
42 }
43
44 FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
45         : _paths (p)
46         , _current_path (0)
47         , _current_file (0)
48 {
49         ensure_open_path (0);
50         seek (0, SEEK_SET);
51 }
52
53 FileGroup::~FileGroup ()
54 {
55         if (_current_file) {
56                 fclose (_current_file);
57         }
58 }
59
60 void
61 FileGroup::set_paths (vector<boost::filesystem::path> const & p)
62 {
63         _paths = p;
64         ensure_open_path (0);
65         seek (0, SEEK_SET);
66 }
67
68 /** Ensure that the given path index in the content is the _current_file */
69 void
70 FileGroup::ensure_open_path (size_t p) const
71 {
72         if (_current_file && _current_path == p) {
73                 /* Already open */
74                 return;
75         }
76         
77         if (_current_file) {
78                 fclose (_current_file);
79         }
80
81         _current_path = p;
82         _current_file = fopen_boost (_paths[_current_path], "rb");
83         if (_current_file == 0) {
84                 throw OpenFileError (_paths[_current_path]);
85         }
86 }
87
88 int64_t
89 FileGroup::seek (int64_t pos, int whence) const
90 {
91         /* Convert pos to `full_pos', which is an offset from the start
92            of all the files.
93         */
94         int64_t full_pos = 0;
95         switch (whence) {
96         case SEEK_SET:
97                 full_pos = pos;
98                 break;
99         case SEEK_CUR:
100                 for (size_t i = 0; i < _current_path; ++i) {
101                         full_pos += boost::filesystem::file_size (_paths[i]);
102                 }
103 #ifdef DCPOMATIC_WINDOWS
104                 full_pos += _ftelli64 (_current_file);
105 #else           
106                 full_pos += ftell (_current_file);
107 #endif          
108                 full_pos += pos;
109                 break;
110         case SEEK_END:
111                 full_pos = length() - pos;
112                 break;
113         }
114
115         /* Seek to full_pos */
116         size_t i = 0;
117         int64_t sub_pos = full_pos;
118         while (i < _paths.size ()) {
119                 boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
120                 if (sub_pos < int64_t (len)) {
121                         break;
122                 }
123                 sub_pos -= len;
124                 ++i;
125         }
126
127         if (i == _paths.size ()) {
128                 return -1;
129         }
130
131         ensure_open_path (i);
132         dcpomatic_fseek (_current_file, sub_pos, SEEK_SET);
133         return full_pos;
134 }
135
136 /** Try to read some data from the current position into a buffer.
137  *  @param buffer Buffer to write data into.
138  *  @param amount Number of bytes to read.
139  *  @return Number of bytes read, or -1 in the case of error.
140  */
141 int
142 FileGroup::read (uint8_t* buffer, int amount) const
143 {
144         int read = 0;
145         while (1) {
146                 int const this_time = fread (buffer + read, 1, amount - read, _current_file);
147                 read += this_time;
148                 if (read == amount) {
149                         /* Done */
150                         break;
151                 }
152
153                 /* See if there is another file to use */
154                 if ((_current_path + 1) >= _paths.size()) {
155                         break;
156                 }
157                 ensure_open_path (_current_path + 1);
158         }
159
160         return read;
161 }
162
163 int64_t
164 FileGroup::length () const
165 {
166         int64_t len = 0;
167         for (size_t i = 0; i < _paths.size(); ++i) {
168                 len += boost::filesystem::file_size (_paths[i]);
169         }
170
171         return len;
172 }