Merge branch '1.0' into 1.0-vob
[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
25 using std::vector;
26 using std::cout;
27
28 FileGroup::FileGroup ()
29         : _current_path (0)
30         , _current_file (0)
31 {
32
33 }
34
35 FileGroup::FileGroup (boost::filesystem::path p)
36         : _current_path (0)
37         , _current_file (0)
38 {
39         _paths.push_back (p);
40         seek (0, SEEK_SET);
41 }
42
43 FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
44         : _paths (p)
45         , _current_path (0)
46         , _current_file (0)
47 {
48         ensure_open_path (0);
49         seek (0, SEEK_SET);
50 }
51
52 FileGroup::~FileGroup ()
53 {
54         if (_current_file) {
55                 fclose (_current_file);
56         }
57 }
58
59 void
60 FileGroup::set_paths (vector<boost::filesystem::path> const & p)
61 {
62         _paths = p;
63         ensure_open_path (0);
64         seek (0, SEEK_SET);
65 }
66
67 /** Ensure that the given path index in the content is the _current_file */
68 void
69 FileGroup::ensure_open_path (size_t p) const
70 {
71         if (_current_file && _current_path == p) {
72                 /* Already open */
73                 return;
74         }
75         
76         if (_current_file) {
77                 fclose (_current_file);
78         }
79
80         _current_path = p;
81         _current_file = fopen (_paths[_current_path].string().c_str(), "rb");
82         if (_current_file == 0) {
83                 throw OpenFileError (_paths[_current_path]);
84         }
85 }
86
87 int64_t
88 FileGroup::seek (int64_t pos, int whence) const
89 {
90         /* Convert pos to `full_pos', which is an offset from the start
91            of all the files.
92         */
93         int64_t full_pos = 0;
94         switch (whence) {
95         case SEEK_SET:
96                 full_pos = pos;
97                 break;
98         case SEEK_CUR:
99                 for (size_t i = 0; i < _current_path; ++i) {
100                         full_pos += boost::filesystem::file_size (_paths[i]);
101                 }
102                 full_pos += ftell (_current_file);
103                 full_pos += pos;
104                 break;
105         case SEEK_END:
106                 full_pos = length() - pos;
107                 break;
108         }
109
110         /* Seek to full_pos */
111         size_t i = 0;
112         int64_t sub_pos = full_pos;
113         while (i < _paths.size ()) {
114                 boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
115                 if (sub_pos < int64_t (len)) {
116                         break;
117                 }
118                 sub_pos -= len;
119                 ++i;
120         }
121
122         if (i == _paths.size ()) {
123                 return -1;
124         }
125
126         ensure_open_path (i);
127         fseek (_current_file, sub_pos, SEEK_SET);
128         return full_pos;
129 }
130
131 /** Try to read some data from the current position into a buffer.
132  *  @param buffer Buffer to write data into.
133  *  @param amount Number of bytes to read.
134  *  @return Number of bytes read, or -1 in the case of error.
135  */
136 int
137 FileGroup::read (uint8_t* buffer, int amount) const
138 {
139         int read = 0;
140         while (1) {
141                 int const this_time = fread (buffer + read, 1, amount - read, _current_file);
142                 read += this_time;
143                 if (read == amount) {
144                         /* Done */
145                         break;
146                 }
147
148                 /* See if there is another file to use */
149                 if ((_current_path + 1) >= _paths.size()) {
150                         break;
151                 }
152                 ensure_open_path (_current_path + 1);
153         }
154
155         return read;
156 }
157
158 int64_t
159 FileGroup::length () const
160 {
161         int64_t len = 0;
162         for (size_t i = 0; i < _paths.size(); ++i) {
163                 len += boost::filesystem::file_size (_paths[i]);
164         }
165
166         return len;
167 }