Missed update to private test repo version.
[dcpomatic.git] / src / lib / file_group.cc
1 /*
2     Copyright (C) 2013-2018 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 /** @file  src/lib/file_group.cc
22  *  @brief FileGroup class.
23  */
24
25 #include "file_group.h"
26 #include "exceptions.h"
27 #include "cross.h"
28 #include "compose.hpp"
29 #include <sndfile.h>
30 #include <cstdio>
31 #include <iostream>
32
33 using std::vector;
34 using std::cout;
35
36 /** Construct a FileGroup with no files */
37 FileGroup::FileGroup ()
38         : _current_path (0)
39         , _current_file (0)
40 {
41
42 }
43
44 /** Construct a FileGroup with a single file */
45 FileGroup::FileGroup (boost::filesystem::path p)
46         : _current_path (0)
47         , _current_file (0)
48 {
49         _paths.push_back (p);
50         ensure_open_path (0);
51         seek (0, SEEK_SET);
52 }
53
54 /** Construct a FileGroup with multiple files */
55 FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
56         : _paths (p)
57         , _current_path (0)
58         , _current_file (0)
59 {
60         ensure_open_path (0);
61         seek (0, SEEK_SET);
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 void
73 FileGroup::set_paths (vector<boost::filesystem::path> const & p)
74 {
75         _paths = p;
76         ensure_open_path (0);
77         seek (0, SEEK_SET);
78 }
79
80 /** Ensure that the given path index in the content is the _current_file */
81 void
82 FileGroup::ensure_open_path (size_t p) const
83 {
84         if (_current_file && _current_path == p) {
85                 /* Already open */
86                 return;
87         }
88
89         if (_current_file) {
90                 fclose (_current_file);
91         }
92
93         _current_path = p;
94         _current_file = fopen_boost (_paths[_current_path], "rb");
95         if (_current_file == 0) {
96                 throw OpenFileError (_paths[_current_path], errno, OpenFileError::READ);
97         }
98 }
99
100 int64_t
101 FileGroup::seek (int64_t pos, int whence) const
102 {
103         /* Convert pos to `full_pos', which is an offset from the start
104            of all the files.
105         */
106         int64_t full_pos = 0;
107         switch (whence) {
108         case SEEK_SET:
109                 full_pos = pos;
110                 break;
111         case SEEK_CUR:
112                 for (size_t i = 0; i < _current_path; ++i) {
113                         full_pos += boost::filesystem::file_size (_paths[i]);
114                 }
115 #ifdef DCPOMATIC_WINDOWS
116                 full_pos += _ftelli64 (_current_file);
117 #else
118                 full_pos += ftell (_current_file);
119 #endif
120                 full_pos += pos;
121                 break;
122         case SEEK_END:
123                 full_pos = length() - pos;
124                 break;
125         }
126
127         /* Seek to full_pos */
128         size_t i = 0;
129         int64_t sub_pos = full_pos;
130         while (i < _paths.size ()) {
131                 boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
132                 if (sub_pos < int64_t (len)) {
133                         break;
134                 }
135                 ++i;
136                 if (i < _paths.size()) {
137                         /* If we've run out of files we need to seek off the end of the last file */
138                         sub_pos -= len;
139                 }
140         }
141
142         if (i == _paths.size ()) {
143                 /* Seeking too far isn't an error; we'll seek too far in the last file which
144                  * will "pass on" fseek()'s behaviour to our caller.
145                  */
146                 i--;
147         }
148
149         ensure_open_path (i);
150         dcpomatic_fseek (_current_file, sub_pos, SEEK_SET);
151         return full_pos;
152 }
153
154 /** Try to read some data from the current position into a buffer.
155  *  @param buffer Buffer to write data into.
156  *  @param amount Number of bytes to read.
157  *  @return Number of bytes read.
158  */
159 int
160 FileGroup::read (uint8_t* buffer, int amount) const
161 {
162         int read = 0;
163         while (true) {
164                 int const this_time = fread (buffer + read, 1, amount - read, _current_file);
165                 read += this_time;
166                 if (read == amount) {
167                         /* Done */
168                         break;
169                 }
170
171                 if (ferror(_current_file)) {
172                         throw FileError (String::compose("fread error %1", errno), _paths[_current_path]);
173                 }
174
175                 if (feof (_current_file)) {
176                         /* See if there is another file to use */
177                         if ((_current_path + 1) >= _paths.size()) {
178                                 break;
179                         }
180                         ensure_open_path (_current_path + 1);
181                 }
182         }
183
184         return read;
185 }
186
187 /** @return Combined length of all the files */
188 int64_t
189 FileGroup::length () const
190 {
191         int64_t len = 0;
192         for (size_t i = 0; i < _paths.size(); ++i) {
193                 len += boost::filesystem::file_size (_paths[i]);
194         }
195
196         return len;
197 }