-rw-r--r-- | src/iii-extract-riff-chunk.cc | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/src/iii-extract-riff-chunk.cc b/src/iii-extract-riff-chunk.cc index 3a06db2..d53170d 100644 --- a/src/iii-extract-riff-chunk.cc +++ b/src/iii-extract-riff-chunk.cc | |||
@@ -1,246 +1,246 @@ | |||
1 | /* vim:set sw=8 nosi noai cin cino=:0,l1,g0: */ | 1 | /* vim:set sw=8 nosi noai cin cino=:0,l1,g0: */ |
2 | #include <stdint.h> | 2 | #include <stdint.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <getopt.h> | 4 | #include <getopt.h> |
5 | #include <cstring> | 5 | #include <cstring> |
6 | #include <iostream> | 6 | #include <iostream> |
7 | #include <fstream> | 7 | #include <fstream> |
8 | #include <stdexcept> | 8 | #include <stdexcept> |
9 | #include <cassert> | 9 | #include <cassert> |
10 | #include <list> | 10 | #include <list> |
11 | #include <string> | 11 | #include <string> |
12 | #include <iterator> | 12 | #include <iterator> |
13 | 13 | ||
14 | #include "config.h" | 14 | #include "config.h" |
15 | 15 | ||
16 | #define PHEADER \ | 16 | #define PHEADER \ |
17 | PACKAGE " Version " VERSION "\n" \ | 17 | PACKAGE " Version " VERSION "\n" \ |
18 | "Copyright (c) 2009-2010 Klever Group" | 18 | "Copyright (c) 2009-2011 Klever Group" |
19 | 19 | ||
20 | typedef uint32_t fourcc_type; | 20 | typedef uint32_t fourcc_type; |
21 | enum fourcc_value { | 21 | enum fourcc_value { |
22 | fourcc_RIFF = 0x46464952, fourcc_AVI = 0x20495641, fourcc_LIST = 0x5453494c, | 22 | fourcc_RIFF = 0x46464952, fourcc_AVI = 0x20495641, fourcc_LIST = 0x5453494c, |
23 | fourcc_hdrl = 0x6c726468, fourcc_strl = 0x6c727473, | 23 | fourcc_hdrl = 0x6c726468, fourcc_strl = 0x6c727473, |
24 | fourcc_ncdt = 0x7464636e, fourcc_ncvr = 0x7276636e, fourcc_nctg = 0x6774636e, fourcc_ncth = 0x6874636e | 24 | fourcc_ncdt = 0x7464636e, fourcc_ncvr = 0x7276636e, fourcc_nctg = 0x6774636e, fourcc_ncth = 0x6874636e |
25 | }; | 25 | }; |
26 | 26 | ||
27 | fourcc_type str2fourcc(const char *str) { | 27 | fourcc_type str2fourcc(const char *str) { |
28 | fourcc_type rv = 0; | 28 | fourcc_type rv = 0; |
29 | return *(fourcc_type*)strncpy((char*)&rv,str,sizeof(rv)); | 29 | return *(fourcc_type*)strncpy((char*)&rv,str,sizeof(rv)); |
30 | } | 30 | } |
31 | const std::string fourcc2str(fourcc_type fcc) { | 31 | const std::string fourcc2str(fourcc_type fcc) { |
32 | char rv[sizeof(fcc)+1]; | 32 | char rv[sizeof(fcc)+1]; |
33 | *(fourcc_type*)rv = fcc; | 33 | *(fourcc_type*)rv = fcc; |
34 | rv[sizeof(fcc)]=0; | 34 | rv[sizeof(fcc)]=0; |
35 | return rv; | 35 | return rv; |
36 | } | 36 | } |
37 | 37 | ||
38 | #pragma pack(1) | 38 | #pragma pack(1) |
39 | struct riff_sized_head { | 39 | struct riff_sized_head { |
40 | fourcc_type fourcc; | 40 | fourcc_type fourcc; |
41 | uint32_t size; | 41 | uint32_t size; |
42 | }; | 42 | }; |
43 | #pragma pack() | 43 | #pragma pack() |
44 | 44 | ||
45 | class exceptional_success : public std::exception { }; | 45 | class exceptional_success : public std::exception { }; |
46 | 46 | ||
47 | struct riff { | 47 | struct riff { |
48 | struct chunk_type { | 48 | struct chunk_type { |
49 | riff_sized_head head; | 49 | riff_sized_head head; |
50 | uint32_t left; | 50 | uint32_t left; |
51 | 51 | ||
52 | chunk_type(riff& r) { | 52 | chunk_type(riff& r) { |
53 | r.read(&head); | 53 | r.read(&head); |
54 | left = head.size; | 54 | left = head.size; |
55 | } | 55 | } |
56 | }; | 56 | }; |
57 | 57 | ||
58 | std::istream *in; | 58 | std::istream *in; |
59 | typedef std::list<chunk_type> chunks_type; | 59 | typedef std::list<chunk_type> chunks_type; |
60 | chunks_type chunks; | 60 | chunks_type chunks; |
61 | 61 | ||
62 | riff(std::istream& i) : in(&i) { } | 62 | riff(std::istream& i) : in(&i) { } |
63 | 63 | ||
64 | protected: | 64 | protected: |
65 | 65 | ||
66 | int begin_chunk() { | 66 | int begin_chunk() { |
67 | chunks.push_back( chunk_type(*this) ); | 67 | chunks.push_back( chunk_type(*this) ); |
68 | return chunks.size(); | 68 | return chunks.size(); |
69 | } | 69 | } |
70 | void end_chunk(int level) { | 70 | void end_chunk(int level) { |
71 | assert(in); assert(chunks.size()==level); | 71 | assert(in); assert(chunks.size()==level); |
72 | std::streamsize o = chunks.back().left; | 72 | std::streamsize o = chunks.back().left; |
73 | chunks.pop_back(); | 73 | chunks.pop_back(); |
74 | if(!o) return; | 74 | if(!o) return; |
75 | in->seekg(o,std::ios::cur); | 75 | in->seekg(o,std::ios::cur); |
76 | for(chunks_type::iterator i=chunks.begin(),ie=chunks.end() ;i!=ie; (i++)->left -= o) ; | 76 | for(chunks_type::iterator i=chunks.begin(),ie=chunks.end() ;i!=ie; (i++)->left -= o) ; |
77 | } | 77 | } |
78 | 78 | ||
79 | void read(void *p,size_t n) { | 79 | void read(void *p,size_t n) { |
80 | assert(in); | 80 | assert(in); |
81 | if( (!chunks.empty()) && chunks.back().left < n) | 81 | if( (!chunks.empty()) && chunks.back().left < n) |
82 | throw std::runtime_error("attempt to read beyond the end of chunk"); | 82 | throw std::runtime_error("attempt to read beyond the end of chunk"); |
83 | if(!in->read((char*)p,n)) | 83 | if(!in->read((char*)p,n)) |
84 | throw std::runtime_error("failed to read()"); | 84 | throw std::runtime_error("failed to read()"); |
85 | std::streamsize gc = in->gcount(); | 85 | std::streamsize gc = in->gcount(); |
86 | for(chunks_type::iterator i=chunks.begin(),ie=chunks.end();i!=ie;++i) { | 86 | for(chunks_type::iterator i=chunks.begin(),ie=chunks.end();i!=ie;++i) { |
87 | i->left -= gc; assert(i->left >= 0); | 87 | i->left -= gc; assert(i->left >= 0); |
88 | } | 88 | } |
89 | if(gc!=n) throw std::runtime_error("incomplete read()"); | 89 | if(gc!=n) throw std::runtime_error("incomplete read()"); |
90 | } | 90 | } |
91 | 91 | ||
92 | template<typename T> void read(T* v) { read(v,sizeof(*v)); } | 92 | template<typename T> void read(T* v) { read(v,sizeof(*v)); } |
93 | 93 | ||
94 | friend class scoped_chunk; | 94 | friend class scoped_chunk; |
95 | 95 | ||
96 | }; | 96 | }; |
97 | 97 | ||
98 | struct scoped_chunk { | 98 | struct scoped_chunk { |
99 | riff& rs; | 99 | riff& rs; |
100 | int level; | 100 | int level; |
101 | riff::chunks_type::reverse_iterator chunk_iterator; | 101 | riff::chunks_type::reverse_iterator chunk_iterator; |
102 | 102 | ||
103 | scoped_chunk(riff& rs_) : rs(rs_), level(rs.begin_chunk()), chunk_iterator(rs.chunks.rbegin()) { } | 103 | scoped_chunk(riff& rs_) : rs(rs_), level(rs.begin_chunk()), chunk_iterator(rs.chunks.rbegin()) { } |
104 | ~scoped_chunk() { rs.end_chunk(level); } | 104 | ~scoped_chunk() { rs.end_chunk(level); } |
105 | 105 | ||
106 | riff::chunk_type& chunk() { return *chunk_iterator; } | 106 | riff::chunk_type& chunk() { return *chunk_iterator; } |
107 | 107 | ||
108 | fourcc_type get_chunk_id() { return chunk().head.fourcc; } | 108 | fourcc_type get_chunk_id() { return chunk().head.fourcc; } |
109 | bool has(uint32_t bytes=1) { return chunk().left >= bytes; } | 109 | bool has(uint32_t bytes=1) { return chunk().left >= bytes; } |
110 | 110 | ||
111 | template<typename T> T read() { T rv; rs.read(&rv); return rv; } | 111 | template<typename T> T read() { T rv; rs.read(&rv); return rv; } |
112 | template<typename T> void read(T& t) { rs.read(&t); } | 112 | template<typename T> void read(T& t) { rs.read(&t); } |
113 | void read(void *p,size_t n) { rs.read(p,n); } | 113 | void read(void *p,size_t n) { rs.read(p,n); } |
114 | 114 | ||
115 | }; | 115 | }; |
116 | 116 | ||
117 | struct chunk_path_type : public std::list<std::string> { | 117 | struct chunk_path_type : public std::list<std::string> { |
118 | 118 | ||
119 | chunk_path_type() { } | 119 | chunk_path_type() { } |
120 | chunk_path_type(const char *str) { | 120 | chunk_path_type(const char *str) { |
121 | for(const char *p0=str,*p1=strchr(p0,'/');p0;p1=p1?strchr(p0=++p1,'/'):(p0=0)) | 121 | for(const char *p0=str,*p1=strchr(p0,'/');p0;p1=p1?strchr(p0=++p1,'/'):(p0=0)) |
122 | if(p0!=p1) | 122 | if(p0!=p1) |
123 | push_back( p1? std::string(p0,p1-p0) : std::string(p0) ); | 123 | push_back( p1? std::string(p0,p1-p0) : std::string(p0) ); |
124 | } | 124 | } |
125 | 125 | ||
126 | }; | 126 | }; |
127 | 127 | ||
128 | struct chunk_walker { | 128 | struct chunk_walker { |
129 | riff& r; | 129 | riff& r; |
130 | chunk_path_type path; | 130 | chunk_path_type path; |
131 | 131 | ||
132 | chunk_walker(riff& r_) : r(r_) { } | 132 | chunk_walker(riff& r_) : r(r_) { } |
133 | 133 | ||
134 | void shoot(scoped_chunk& chunk) { | 134 | void shoot(scoped_chunk& chunk) { |
135 | riff::chunk_type& c = chunk.chunk(); | 135 | riff::chunk_type& c = chunk.chunk(); |
136 | while(c.left) { | 136 | while(c.left) { |
137 | static char tmp[512]; | 137 | static char tmp[512]; |
138 | int r = (c.left<sizeof(tmp)) ? c.left : sizeof(tmp) ; | 138 | int r = (c.left<sizeof(tmp)) ? c.left : sizeof(tmp) ; |
139 | chunk.read(tmp,r); std::cout.write(tmp,r); | 139 | chunk.read(tmp,r); std::cout.write(tmp,r); |
140 | } | 140 | } |
141 | if(first) throw exceptional_success(); | 141 | if(first) throw exceptional_success(); |
142 | } | 142 | } |
143 | 143 | ||
144 | void walk_chunks() { | 144 | void walk_chunks() { |
145 | scoped_chunk chunk(r); | 145 | scoped_chunk chunk(r); |
146 | std::string pc = fourcc2str(chunk.get_chunk_id()); | 146 | std::string pc = fourcc2str(chunk.get_chunk_id()); |
147 | bool list = false; | 147 | bool list = false; |
148 | switch(chunk.get_chunk_id()) { | 148 | switch(chunk.get_chunk_id()) { |
149 | case fourcc_RIFF: case fourcc_LIST: | 149 | case fourcc_RIFF: case fourcc_LIST: |
150 | list = true; | 150 | list = true; |
151 | pc += '.'; pc += fourcc2str(chunk.read<fourcc_type>()); | 151 | pc += '.'; pc += fourcc2str(chunk.read<fourcc_type>()); |
152 | break; | 152 | break; |
153 | } | 153 | } |
154 | path.push_back(pc); | 154 | path.push_back(pc); |
155 | std::pair<chunk_path_type::const_iterator,chunk_path_type::const_iterator> | 155 | std::pair<chunk_path_type::const_iterator,chunk_path_type::const_iterator> |
156 | mm = std::mismatch(path.begin(),path.end(),target.begin()); | 156 | mm = std::mismatch(path.begin(),path.end(),target.begin()); |
157 | bool dive = list; | 157 | bool dive = list; |
158 | if(mm.first==path.end()) { | 158 | if(mm.first==path.end()) { |
159 | if(mm.second==target.end()) { | 159 | if(mm.second==target.end()) { |
160 | shoot(chunk); | 160 | shoot(chunk); |
161 | dive = false; | 161 | dive = false; |
162 | } | 162 | } |
163 | }else{ | 163 | }else{ |
164 | assert(mm.second!=target.end()); | 164 | assert(mm.second!=target.end()); |
165 | dive = false; | 165 | dive = false; |
166 | } | 166 | } |
167 | 167 | ||
168 | if(dive) while(chunk.has()) walk_chunks(); | 168 | if(dive) while(chunk.has()) walk_chunks(); |
169 | path.pop_back(); | 169 | path.pop_back(); |
170 | } | 170 | } |
171 | 171 | ||
172 | 172 | ||
173 | chunk_path_type target; | 173 | chunk_path_type target; |
174 | chunk_walker& set_target(chunk_path_type t) { target = t; return *this; } | 174 | chunk_walker& set_target(chunk_path_type t) { target = t; return *this; } |
175 | bool first; | 175 | bool first; |
176 | chunk_walker& set_oneshot(bool os) { first = true; return *this; } | 176 | chunk_walker& set_oneshot(bool os) { first = true; return *this; } |
177 | }; | 177 | }; |
178 | 178 | ||
179 | void usage(char **argv) { | 179 | void usage(char **argv) { |
180 | std::cerr << PHEADER << std::endl << std::endl | 180 | std::cerr << PHEADER << std::endl << std::endl |
181 | << ' ' << argv[0] << " [options] <avi-file> <chunk-path>" << std::endl | 181 | << ' ' << argv[0] << " [options] <avi-file> <chunk-path>" << std::endl |
182 | << std::endl << | 182 | << std::endl << |
183 | " -h, --help,\n" | 183 | " -h, --help,\n" |
184 | " --usage display this text\n" | 184 | " --usage display this text\n" |
185 | " -V, --version display version information\n" | 185 | " -V, --version display version information\n" |
186 | " -L, --license show license\n" | 186 | " -L, --license show license\n" |
187 | "\n" | 187 | "\n" |
188 | "Example: " << argv[0] << " DSC_0001.AVI '/RIFF.AVI /LIST.ncdt/nctg' \\\n" | 188 | "Example: " << argv[0] << " DSC_0001.AVI '/RIFF.AVI /LIST.ncdt/nctg' \\\n" |
189 | "\t\t| dd bs=1 skip=82 count=19 2>/dev/null\n" | 189 | "\t\t| dd bs=1 skip=82 count=19 2>/dev/null\n" |
190 | "Output: YYYY:MM:DD HH:MM:SS (for Nikon-recorded AVI)" | 190 | "Output: YYYY:MM:DD HH:MM:SS (for Nikon-recorded AVI)" |
191 | << std::endl << std::endl; | 191 | << std::endl << std::endl; |
192 | exit(0); | 192 | exit(0); |
193 | } | 193 | } |
194 | 194 | ||
195 | int main(int argc,char **argv) try { | 195 | int main(int argc,char **argv) try { |
196 | 196 | ||
197 | bool first = false; | 197 | bool first = false; |
198 | 198 | ||
199 | for(;;) { | 199 | for(;;) { |
200 | static struct option opts[] = { | 200 | static struct option opts[] = { |
201 | { "first", no_argument, 0, '1' }, | 201 | { "first", no_argument, 0, '1' }, |
202 | { "help", no_argument, 0, 'h' }, | 202 | { "help", no_argument, 0, 'h' }, |
203 | { "usage", no_argument, 0, 'h' }, | 203 | { "usage", no_argument, 0, 'h' }, |
204 | { "version", no_argument, 0, 'V' }, | 204 | { "version", no_argument, 0, 'V' }, |
205 | { "license", no_argument, 0, 'L' }, | 205 | { "license", no_argument, 0, 'L' }, |
206 | { NULL, 0, 0, 0 } | 206 | { NULL, 0, 0, 0 } |
207 | }; | 207 | }; |
208 | int c = getopt_long(argc,argv,"1hVL",opts,NULL); | 208 | int c = getopt_long(argc,argv,"1hVL",opts,NULL); |
209 | if(c==-1) break; | 209 | if(c==-1) break; |
210 | switch(c) { | 210 | switch(c) { |
211 | case 'h': | 211 | case 'h': |
212 | usage(argv); break; | 212 | usage(argv); break; |
213 | case 'V': | 213 | case 'V': |
214 | std::cerr << VERSION << std::endl; | 214 | std::cerr << VERSION << std::endl; |
215 | exit(0); break; | 215 | exit(0); break; |
216 | case 'L': | 216 | case 'L': |
217 | extern const char *COPYING; | 217 | extern const char *COPYING; |
218 | std::cerr << COPYING << std::endl; | 218 | std::cerr << COPYING << std::endl; |
219 | exit(0); break; | 219 | exit(0); break; |
220 | case '1': | 220 | case '1': |
221 | first = true; break; | 221 | first = true; break; |
222 | default: | 222 | default: |
223 | std::cerr << "Huh?" << std::endl; | 223 | std::cerr << "Huh?" << std::endl; |
224 | exit(1); break; | 224 | exit(1); break; |
225 | } | 225 | } |
226 | } | 226 | } |
227 | 227 | ||
228 | if(optind!=(argc-2)) usage(argv); | 228 | if(optind!=(argc-2)) usage(argv); |
229 | 229 | ||
230 | std::ifstream i(argv[optind],std::ios::in); | 230 | std::ifstream i(argv[optind],std::ios::in); |
231 | if(!i) { | 231 | if(!i) { |
232 | std::cerr << "Failed to open file '" << argv[optind] << "'" << std::endl; | 232 | std::cerr << "Failed to open file '" << argv[optind] << "'" << std::endl; |
233 | exit(1); | 233 | exit(1); |
234 | } | 234 | } |
235 | riff rs(i); | 235 | riff rs(i); |
236 | chunk_walker(rs) | 236 | chunk_walker(rs) |
237 | .set_target(chunk_path_type(argv[++optind])) | 237 | .set_target(chunk_path_type(argv[++optind])) |
238 | .set_oneshot(first) | 238 | .set_oneshot(first) |
239 | .walk_chunks(); | 239 | .walk_chunks(); |
240 | return 0; | 240 | return 0; |
241 | }catch(exceptional_success&) { | 241 | }catch(exceptional_success&) { |
242 | return 0; | 242 | return 0; |
243 | }catch(std::exception &e) { | 243 | }catch(std::exception &e) { |
244 | std::cerr << "oops: " << e.what() << std::endl; | 244 | std::cerr << "oops: " << e.what() << std::endl; |
245 | return 1; | 245 | return 1; |
246 | } | 246 | } |