2 Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
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.
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.
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/>.
21 /** @file src/lib/file_group.cc
22 * @brief FileGroup class.
25 #include "file_group.h"
26 #include "exceptions.h"
28 #include "compose.hpp"
36 /** Construct a FileGroup with no files */
37 FileGroup::FileGroup ()
45 /** Construct a FileGroup with a single file */
46 FileGroup::FileGroup (boost::filesystem::path p)
56 /** Construct a FileGroup with multiple files */
57 FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
66 /** Destroy a FileGroup, closing any open file */
67 FileGroup::~FileGroup ()
70 fclose (_current_file);
75 FileGroup::set_paths (vector<boost::filesystem::path> const & p)
82 /** Ensure that the given path index in the content is the _current_file */
84 FileGroup::ensure_open_path (size_t p) const
86 if (_current_file && _current_path == p) {
92 fclose (_current_file);
96 _current_file = fopen_boost (_paths[_current_path], "rb");
97 if (_current_file == 0) {
98 throw OpenFileError (_paths[_current_path], errno, OpenFileError::READ);
100 _current_size = boost::filesystem::file_size (_paths[_current_path]);
104 FileGroup::seek (int64_t pos, int whence) const
106 /* Convert pos to `full_pos', which is an offset from the start
109 int64_t full_pos = 0;
115 for (size_t i = 0; i < _current_path; ++i) {
116 full_pos += boost::filesystem::file_size (_paths[i]);
118 #ifdef DCPOMATIC_WINDOWS
119 full_pos += _ftelli64 (_current_file);
121 full_pos += ftell (_current_file);
126 full_pos = length() - pos;
130 /* Seek to full_pos */
132 int64_t sub_pos = full_pos;
133 while (i < _paths.size ()) {
134 boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
135 if (sub_pos < int64_t (len)) {
139 if (i < _paths.size()) {
140 /* If we've run out of files we need to seek off the end of the last file */
145 if (i == _paths.size ()) {
146 /* Seeking too far isn't an error; we'll seek too far in the last file which
147 * will "pass on" fseek()'s behaviour to our caller.
152 ensure_open_path (i);
153 dcpomatic_fseek (_current_file, sub_pos, SEEK_SET);
157 /** Try to read some data from the current position into a buffer.
158 * @param buffer Buffer to write data into.
159 * @param amount Number of bytes to read.
160 * @return Number of bytes read.
163 FileGroup::read (uint8_t* buffer, int amount) const
167 int64_t to_read = amount - read;
168 #ifdef DCPOMATIC_WINDOWS
169 /* If we over-read from the file by too much on Windows we get a errno=22 rather than an feof condition,
170 * for unknown reasons. So if we're going to over-read, we need to do it by a little bit, so that feof
171 * still gets triggered but there is no errno=22.
173 to_read = std::min(to_read, static_cast<int64_t>(_current_size - _ftelli64(_current_file) + 1));
175 int const this_time = fread (buffer + read, 1, to_read, _current_file);
177 if (read == amount) {
182 if (ferror(_current_file)) {
183 throw FileError (String::compose("fread error %1", errno), _paths[_current_path]);
186 if (feof (_current_file)) {
187 /* See if there is another file to use */
188 if ((_current_path + 1) >= _paths.size()) {
191 ensure_open_path (_current_path + 1);
198 /** @return Combined length of all the files */
200 FileGroup::length () const
203 for (size_t i = 0; i < _paths.size(); ++i) {
204 len += boost::filesystem::file_size (_paths[i]);