Use dcp::file_to_string().
[dcpomatic.git] / src / lib / file_group.cc
1 /*
2     Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 /** @file  src/lib/file_group.cc
23  *  @brief FileGroup class.
24  */
25
26
27 #include "compose.hpp"
28 #include "cross.h"
29 #include "dcpomatic_assert.h"
30 #include "exceptions.h"
31 #include "file_group.h"
32 #include <sndfile.h>
33 #include <cstdio>
34
35
36 using std::vector;
37
38
39 /** Construct a FileGroup with no files */
40 FileGroup::FileGroup ()
41 {
42
43 }
44
45
46 /** Construct a FileGroup with a single file */
47 FileGroup::FileGroup (boost::filesystem::path p)
48 {
49         _paths.push_back (p);
50         ensure_open_path (0);
51         seek (0, SEEK_SET);
52 }
53
54
55 /** Construct a FileGroup with multiple files */
56 FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
57         : _paths (p)
58 {
59         ensure_open_path (0);
60         seek (0, SEEK_SET);
61 }
62
63
64 /** Destroy a FileGroup, closing any open file */
65 FileGroup::~FileGroup ()
66 {
67         if (_current_file) {
68                 fclose (_current_file);
69         }
70 }
71
72
73 void
74 FileGroup::set_paths (vector<boost::filesystem::path> const & p)
75 {
76         _paths = p;
77         ensure_open_path (0);
78         seek (0, SEEK_SET);
79 }
80
81
82 /** Ensure that the given path index in the content is the _current_file */
83 void
84 FileGroup::ensure_open_path (size_t p) const
85 {
86         if (_current_file && _current_path == p) {
87                 /* Already open */
88                 return;
89         }
90
91         if (_current_file) {
92                 fclose (_current_file);
93         }
94
95         _current_path = p;
96         _current_file = fopen_boost (_paths[_current_path], "rb");
97         if (!_current_file) {
98                 throw OpenFileError (_paths[_current_path], errno, OpenFileError::READ);
99         }
100         _current_size = boost::filesystem::file_size (_paths[_current_path]);
101 }
102
103
104 int64_t
105 FileGroup::seek (int64_t pos, int whence) const
106 {
107         switch (whence) {
108         case SEEK_SET:
109                 _position = pos;
110                 break;
111         case SEEK_CUR:
112                 _position += pos;
113                 break;
114         case SEEK_END:
115                 _position = length() - pos;
116                 break;
117         }
118
119         /* Find an offset within one of the files, if _position is within a file */
120         size_t i = 0;
121         int64_t sub_pos = _position;
122         while (i < _paths.size()) {
123                 boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
124                 if (sub_pos < int64_t(len)) {
125                         break;
126                 }
127                 sub_pos -= int64_t(len);
128                 ++i;
129         }
130
131         if (i < _paths.size()) {
132                 ensure_open_path (i);
133                 dcpomatic_fseek (_current_file, sub_pos, SEEK_SET);
134         } else {
135                 ensure_open_path (_paths.size() - 1);
136                 dcpomatic_fseek (_current_file, _current_size, SEEK_SET);
137         }
138
139         return _position;
140 }
141
142
143 /** Try to read some data from the current position into a buffer.
144  *  @param buffer Buffer to write data into.
145  *  @param amount Number of bytes to read.
146  *  @return Number of bytes read.
147  */
148 int
149 FileGroup::read (uint8_t* buffer, int amount) const
150 {
151         int read = 0;
152         while (true) {
153
154                 bool eof = false;
155                 size_t to_read = amount - read;
156
157                 DCPOMATIC_ASSERT (_current_file);
158
159 #ifdef DCPOMATIC_WINDOWS
160                 int64_t const current_position = _ftelli64 (_current_file);
161                 if (current_position == -1) {
162                         to_read = 0;
163                         eof = true;
164                 } else if ((current_position + to_read) > _current_size) {
165                         to_read = _current_size - current_position;
166                         eof = true;
167                 }
168 #else
169                 long const current_position = ftell(_current_file);
170                 if ((current_position + to_read) > _current_size) {
171                         to_read = _current_size - current_position;
172                         eof = true;
173                 }
174 #endif
175
176                 int const this_time = fread (buffer + read, 1, to_read, _current_file);
177                 read += this_time;
178                 _position += this_time;
179                 if (read == amount) {
180                         /* Done */
181                         break;
182                 }
183
184                 if (ferror(_current_file)) {
185                         throw FileError (String::compose("fread error %1", errno), _paths[_current_path]);
186                 }
187
188                 if (eof) {
189                         /* See if there is another file to use */
190                         if ((_current_path + 1) >= _paths.size()) {
191                                 break;
192                         }
193                         ensure_open_path (_current_path + 1);
194                 }
195         }
196
197         return read;
198 }
199
200
201 /** @return Combined length of all the files */
202 int64_t
203 FileGroup::length () const
204 {
205         int64_t len = 0;
206         for (size_t i = 0; i < _paths.size(); ++i) {
207                 len += boost::filesystem::file_size (_paths[i]);
208         }
209
210         return len;
211 }