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