Fix zero luma corner case
[lsnes.git] / src / util / sdmp2sox.cpp
blobb5b94c5e22cb489aa6ccd17cab957e10b0e2d4ef
1 #include <cstdint>
2 #include <iostream>
3 #include <fstream>
4 #include <sstream>
5 #include <stdexcept>
6 #include <cstring>
8 #define FLAG_WIDTH 1
9 #define FLAG_HEIGHT 2
10 #define FLAG_FRAMERATE 4
11 #define FLAG_FULLRANGE 8
12 #define FLAG_ITU601 0
13 #define FLAG_ITU709 16
14 #define FLAG_SMPTE240M 32
15 #define FLAG_CS_MASK 48
16 #define FLAG_8BIT 64
17 #define FLAG_FAKENLARGE 128
18 #define FLAG_DEDUP 256
21 #define MAX_DEDUP 9
23 //Heh, this happens to be exact hardware capacity of 1.44MB 90mm floppy. :-)
24 //This buffer needs to be big enough to store 512x480 16-bit YCbCr 4:4:4 (6 bytes per pixel) image.
25 unsigned char yuv_buffer[1474560];
26 unsigned char old_yuv_buffer[1474560];
28 //30 bit values.
29 uint32_t ymatrix[0x80000];
30 uint32_t cbmatrix[0x80000];
31 uint32_t crmatrix[0x80000];
34 #define TOYUV(src, idx) do {\
35 uint32_t c = (static_cast<uint32_t>(src[(idx) + 0]) << 24) |\
36 (static_cast<uint32_t>(src[(idx) + 1]) << 16) |\
37 (static_cast<uint32_t>(src[(idx) + 2]) << 8) |\
38 static_cast<uint32_t>(src[(idx) + 3]);\
39 Y += ymatrix[c & 0x7FFFF];\
40 Cb += cbmatrix[c & 0x7FFFF];\
41 Cr += crmatrix[c & 0x7FFFF];\
42 } while(0)
44 #define RGB2YUV_SHIFT 14
46 struct store16
48 static const size_t esize = 2;
49 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
50 uint32_t v3)
52 *reinterpret_cast<uint16_t*>(buffer + idx) = (v1 >> RGB2YUV_SHIFT);
53 *reinterpret_cast<uint16_t*>(buffer + idx + psep) = (v2 >> RGB2YUV_SHIFT);
54 *reinterpret_cast<uint16_t*>(buffer + idx + 2 * psep) = (v3 >> RGB2YUV_SHIFT);
58 struct store8
60 static const size_t esize = 1;
61 static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
62 uint32_t v3)
64 buffer[idx] = (v1 >> (RGB2YUV_SHIFT + 8));
65 buffer[idx + psep] = (v2 >> (RGB2YUV_SHIFT + 8));
66 buffer[idx + 2 * psep] = (v3 >> (RGB2YUV_SHIFT + 8));
70 template<class store, size_t ioff1, size_t ioff2, size_t ioff3, size_t ooff1, size_t ooff2, size_t ooff3>
71 struct loadstore
73 static const size_t esize = store::esize;
74 static void convert(unsigned char* obuffer, size_t oidx, const unsigned char* ibuffer, size_t iidx,
75 size_t psep)
77 //Compiler should be able to eliminate every if out of this.
78 uint32_t Y = 0;
79 uint32_t Cb = 0;
80 uint32_t Cr = 0;
81 TOYUV(ibuffer, iidx);
82 if(ioff1 > 0)
83 TOYUV(ibuffer, iidx + ioff1 * 4);
84 if(ioff2 > 0)
85 TOYUV(ibuffer, iidx + ioff2 * 4);
86 if(ioff3 > 0)
87 TOYUV(ibuffer, iidx + ioff3 * 4);
88 if(ioff1 > 0 && ioff2 > 0 && ioff3 > 0) {
89 Y >>= 2;
90 Cb >>= 2;
91 Cr >>= 2;
92 } else if(ioff1 > 0) {
93 Y >>= 1;
94 Cb >>= 1;
95 Cr >>= 1;
97 store::store(obuffer, oidx, psep, Y, Cb, Cr);
98 if(ooff1 > 0)
99 store::store(obuffer, oidx + ooff1 * store::esize, psep, Y, Cb, Cr);
100 if(ooff2 > 0)
101 store::store(obuffer, oidx + ooff2 * store::esize, psep, Y, Cb, Cr);
102 if(ooff3 > 0)
103 store::store(obuffer, oidx + ooff3 * store::esize, psep, Y, Cb, Cr);
107 template<class proc, size_t lim, size_t igap, size_t ogap>
108 struct loop
110 static void f(unsigned char* buffer, const unsigned char* src, size_t psep)
112 for(size_t i = 0; i < lim; i++)
113 proc::convert(buffer, proc::esize * ogap * i, src, 4 * igap * i, psep);
117 //Render a line pair of YUV with 256x224/240
118 template<class store>
119 void render_yuv_256_240(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
121 if(hires)
122 if(interlaced)
123 loop<loadstore<store, 1, 512, 513, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
124 else
125 loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
126 else
127 if(interlaced)
128 loop<loadstore<store, 512, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
129 else
130 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
133 //Render a line pair of YUV with 512x224/240
134 template<class store>
135 void render_yuv_512_240(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
137 if(hires)
138 if(interlaced)
139 loop<loadstore<store, 512, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
140 else
141 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
142 else
143 if(interlaced)
144 loop<loadstore<store, 512, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
145 else
146 loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
149 //Render a line pair of YUV with 256x448/480
150 template<class store>
151 void render_yuv_256_480(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
153 if(hires)
154 if(interlaced) {
155 loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
156 loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer + 256 * store::esize,
157 src + 2048, psep);
158 } else
159 loop<loadstore<store, 1, 0, 0, 256, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
160 else
161 if(interlaced) {
162 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
163 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer + 256 * store::esize,
164 src + 2048, psep);
165 } else
166 loop<loadstore<store, 0, 0, 0, 256, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
169 //Render a line pair of YUV with 512x448/480
170 template<class store>
171 void render_yuv_512_480(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
173 if(hires)
174 if(interlaced) {
175 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
176 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer + 512 * store::esize,
177 src + 2048, psep);
178 } else
179 loop<loadstore<store, 0, 0, 0, 512, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
180 else
181 if(interlaced) {
182 loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
183 loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer + 512 * store::esize,
184 src + 2048, psep);
185 } else
186 loop<loadstore<store, 0, 0, 0, 1, 512, 513>, 256, 1, 2>::f(buffer, src, psep);
189 //Render a line pair of YUV with 512x448/480 fakeexpand
190 template<class store>
191 void render_yuv_fe(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
193 if(hires)
194 if(interlaced) {
195 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
196 loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer + 512 * store::esize,
197 src + 2048, psep);
198 } else
199 loop<loadstore<store, 1, 0, 0, 1, 512, 513>, 256, 2, 2>::f(buffer, src, psep);
200 else
201 if(interlaced) {
202 loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
203 loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer + 512 * store::esize,
204 src + 2048, psep);
205 } else
206 loop<loadstore<store, 0, 0, 0, 1, 256, 257>, 256, 1, 2>::f(buffer, src, psep);
209 void init_matrix(double Kb, double Kr, bool fullrange)
211 double RY = Kr;
212 double GY = 1 - Kr - Kb;
213 double BY = Kb;
214 double RPb = -0.5 * Kr / (1 - Kb);
215 double GPb = -0.5 * (1 - Kr - Kb) / (1 - Kb);
216 double BPb = 0.5;
217 double RPr = 0.5;
218 double GPr = -0.5 * (1 - Kr - Kb) / (1 - Kr);
219 double BPr = -0.5 * Kb / (1 - Kr);
220 for(uint32_t i = 0; i < 0x80000; i++) {
221 uint32_t l = 1 + ((i >> 15) & 0xF);
222 //Range of (r,g,b) is 0...496.
223 uint32_t r = (l * ((i >> 0) & 0x1F));
224 uint32_t g = (l * ((i >> 5) & 0x1F));
225 uint32_t b = (l * ((i >> 10) & 0x1F));
226 double Y = (RY * r + GY * g + BY * b) / 496 * (fullrange ? 255 : 219) + (fullrange ? 0 : 16);
227 double Cb = (RPb * r + GPb * g + BPb * b) / 496 * (fullrange ? 255 : 224) + 128;
228 double Cr = (RPr * r + GPr * g + BPr * b) / 496 * (fullrange ? 255 : 224) + 128;
229 ymatrix[i] = static_cast<uint32_t>(Y * 4194304 + 0.5);
230 cbmatrix[i] = static_cast<uint32_t>(Cb * 4194304 + 0.5);
231 crmatrix[i] = static_cast<uint32_t>(Cr * 4194304 + 0.5);
235 //Load RGB to YUV conversion matrix.
236 void load_rgb2yuv_matrix(uint32_t flags)
238 switch(flags & (FLAG_CS_MASK | FLAG_FULLRANGE))
240 case FLAG_ITU601:
241 init_matrix(0.114, 0.229, false);
242 break;
243 case FLAG_ITU601 | FLAG_FULLRANGE:
244 init_matrix(0.114, 0.229, true);
245 break;
246 case FLAG_ITU709:
247 init_matrix(0.0722, 0.2126, false);
248 break;
249 case FLAG_ITU709 | FLAG_FULLRANGE:
250 init_matrix(0.0722, 0.2126, true);
251 break;
252 case FLAG_SMPTE240M:
253 init_matrix(0.087, 0.212, false);
254 break;
255 case FLAG_SMPTE240M | FLAG_FULLRANGE:
256 init_matrix(0.087, 0.212, true);
257 break;
258 default:
259 init_matrix(0.114, 0.229, false);
260 break;
264 uint64_t double_to_ieeefp(double v)
266 unsigned mag = 1023;
267 while(v >= 2) {
268 mag++;
269 v /= 2;
271 while(v < 1) {
272 mag--;
273 v *= 2;
275 uint64_t v2 = mag;
276 v -= 1;
277 for(unsigned i = 0; i < 52; i++) {
278 v *= 2;
279 v2 = 2 * v2 + ((v >= 1) ? 1 : 0);
280 if(v >= 1)
281 v -= 1;
283 return v2;
286 void sdump2sox(std::istream& in, std::ostream& yout, std::ostream& sout, std::ostream& tout, int32_t flags)
288 unsigned elided = 0;
289 uint64_t ftcw = 0;
290 uint64_t ftcn = 0;
291 if(flags & FLAG_DEDUP)
292 tout << "# timecode format v2" << std::endl;
293 void (*render_yuv)(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced);
294 switch(flags & (FLAG_WIDTH | FLAG_HEIGHT | FLAG_8BIT | FLAG_FAKENLARGE)) {
295 case 0:
296 render_yuv = render_yuv_256_240<store16>;
297 break;
298 case FLAG_WIDTH:
299 render_yuv = render_yuv_512_240<store16>;
300 break;
301 case FLAG_HEIGHT:
302 render_yuv = render_yuv_256_480<store16>;
303 break;
304 case FLAG_WIDTH | FLAG_HEIGHT:
305 render_yuv = render_yuv_512_480<store16>;
306 break;
307 case FLAG_WIDTH | FLAG_HEIGHT | FLAG_FAKENLARGE:
308 render_yuv = render_yuv_fe<store16>;
309 break;
310 case FLAG_8BIT:
311 render_yuv = render_yuv_256_240<store8>;
312 break;
313 case FLAG_WIDTH | FLAG_8BIT:
314 render_yuv = render_yuv_512_240<store8>;
315 break;
316 case FLAG_HEIGHT | FLAG_8BIT:
317 render_yuv = render_yuv_256_480<store8>;
318 break;
319 case FLAG_WIDTH | FLAG_HEIGHT | FLAG_8BIT:
320 render_yuv = render_yuv_512_480<store8>;
321 break;
322 case FLAG_WIDTH | FLAG_HEIGHT | FLAG_FAKENLARGE | FLAG_8BIT:
323 render_yuv = render_yuv_fe<store8>;
324 break;
326 unsigned char header[12];
327 in.read(reinterpret_cast<char*>(header), 12);
328 if(!in)
329 throw std::runtime_error("Can't read sdump header");
330 if(header[0] != 'S' || header[1] != 'D' || header[2] != 'M' || header[3] != 'P')
331 throw std::runtime_error("Bad sdump magic");
332 uint32_t apurate;
333 uint32_t cpurate;
334 cpurate = (static_cast<uint32_t>(header[4]) << 24) |
335 (static_cast<uint32_t>(header[5]) << 16) |
336 (static_cast<uint32_t>(header[6]) << 8) |
337 static_cast<uint32_t>(header[7]);
338 apurate = (static_cast<uint32_t>(header[8]) << 24) |
339 (static_cast<uint32_t>(header[9]) << 16) |
340 (static_cast<uint32_t>(header[10]) << 8) |
341 static_cast<uint32_t>(header[11]);
342 uint64_t sndrateR = double_to_ieeefp(static_cast<double>(apurate) / 768.0);
343 unsigned char sox_header[32] = {0};
344 sox_header[0] = 0x2E; //Magic
345 sox_header[1] = 0x53; //Magic
346 sox_header[2] = 0x6F; //Magic
347 sox_header[3] = 0x58; //Magic
348 sox_header[4] = 0x1C; //Magic
349 sox_header[16] = sndrateR;
350 sox_header[17] = sndrateR >> 8;
351 sox_header[18] = sndrateR >> 16;
352 sox_header[19] = sndrateR >> 24;
353 sox_header[20] = sndrateR >> 32;
354 sox_header[21] = sndrateR >> 40;
355 sox_header[22] = sndrateR >> 48;
356 sox_header[23] = sndrateR >> 56;
357 sox_header[24] = 2;
358 sout.write(reinterpret_cast<char*>(sox_header), 32);
359 if(!sout)
360 throw std::runtime_error("Can't write audio header");
361 uint64_t samples = 0;
362 uint64_t frames = 0;
363 unsigned wrongrate = 0;
364 bool is_pal = false;
365 load_rgb2yuv_matrix(flags);
366 while(true) {
367 unsigned char cmd;
368 bool lf = false;
369 in >> cmd;
370 if(!in)
371 break; //End of stream.
372 if((cmd & 0xF0) == 0) {
373 //Pictrue. Read the 1MiB of picture data one line pair at a time.
374 unsigned char buf[4096];
375 unsigned physline = 0;
376 bool hires = (cmd & 1);
377 bool interlaced = (cmd & 2);
378 bool overscan = (cmd & 4);
379 bool pal = (cmd & 8);
380 bool ohires = (flags & FLAG_WIDTH);
381 bool ointerlaced = (flags & FLAG_HEIGHT);
382 bool bits8 = (flags & FLAG_8BIT);
383 size_t psep = (ohires ? 512 : 256) * (ointerlaced ? 2 : 1) * (pal ? 240 : 224) *
384 (bits8 ? 1 : 2);
385 size_t lsep = (ohires ? 512 : 256) * (ointerlaced ? 2 : 1) * (bits8 ? 1 : 2);
386 for(unsigned i = 0; i < 256; i++) {
387 in.read(reinterpret_cast<char*>(buf), 4096);
388 if(!in)
389 throw std::runtime_error("Can't read picture payload");
390 is_pal = is_pal || pal;
391 if(overscan && i < 9)
392 continue;
393 if(!overscan && i < 1)
394 continue;
395 if(pal & physline >= 239)
396 continue;
397 if(!pal & physline >= 224)
398 continue;
399 render_yuv(yuv_buffer + physline * lsep, buf, psep, hires, interlaced);
400 physline++;
402 if(pal) {
403 //Render a black line to pad the image.
404 memset(buf, 0, 4096);
405 render_yuv(yuv_buffer + 239 * lsep, buf, psep, hires, interlaced);
407 size_t yuvsize = 3 * psep;
408 unsigned times = 1;
409 //If FLAG_DEDUP is set, no frames are added or dropped to match timecodes.
410 if((flags & (FLAG_FRAMERATE | FLAG_DEDUP)) == 0 && !is_pal && interlaced) {
411 //This uses 357368 TU instead of 357366 TU.
412 //-> Every 178683rd frame is duplicated.
413 if(wrongrate == 178682) {
414 times = 2;
415 wrongrate = 0;
416 } else
417 wrongrate++;
419 if((flags & (FLAG_FRAMERATE | FLAG_DEDUP)) == FLAG_FRAMERATE && !is_pal && !interlaced) {
420 //This uses 357366 TU instead of 357368 TU.
421 //-> Every 178684th frame is dropped.
422 if(wrongrate == 178683) {
423 times = 0;
424 wrongrate = 0;
425 } else
426 wrongrate++;
428 if(flags & FLAG_DEDUP) {
429 if(memcmp(old_yuv_buffer, yuv_buffer, yuvsize)) {
430 memcpy(old_yuv_buffer, yuv_buffer, yuvsize);
431 elided = 0;
432 } else
433 elided = (++elided) % MAX_DEDUP;
434 if(elided)
435 times = 0;
436 else
437 tout << ftcw << std::endl;
439 for(unsigned k = 0; k < times; k++)
440 yout.write(reinterpret_cast<char*>(yuv_buffer), yuvsize);
441 if(!yout)
442 throw std::runtime_error("Can't write frame");
443 frames += times;
444 lf = true;
445 uint64_t tcc = is_pal ? 425568000 : (interlaced ? 357368000 : 357366000);
446 ftcw = ftcw + tcc / cpurate;
447 ftcn = ftcn + tcc % cpurate;
448 if(ftcn >= cpurate) {
449 ftcw++;
450 ftcn -= cpurate;
452 } else if(cmd == 16) {
453 //Sound packet. Interesting.
454 unsigned char ibuf[4];
455 unsigned char obuf[8];
456 in.read(reinterpret_cast<char*>(ibuf), 4);
457 if(!in)
458 throw std::runtime_error("Can't read sound packet payload");
459 obuf[0] = 0;
460 obuf[1] = 0;
461 obuf[2] = ibuf[1];
462 obuf[3] = ibuf[0];
463 obuf[4] = 0;
464 obuf[5] = 0;
465 obuf[6] = ibuf[3];
466 obuf[7] = ibuf[2];
467 sout.write(reinterpret_cast<char*>(obuf), 8);
468 if(!sout)
469 throw std::runtime_error("Can't write audio sample");
470 samples++;
471 } else {
472 std::ostringstream str;
473 str << "Unknown command byte " << static_cast<unsigned>(cmd);
474 throw std::runtime_error(str.str());
476 if(lf && frames % 100 == 0) {
477 std::cout << "\e[1G" << frames << " frames, " << samples << " samples." << std::flush;
480 //Sox internally multiplies sample count by channel count.
481 sox_header[8] = samples << 1;
482 sox_header[9] = samples >> 7;
483 sox_header[10] = samples >> 15;
484 sox_header[11] = samples >> 23;
485 sox_header[12] = samples >> 31;
486 sox_header[13] = samples >> 39;
487 sox_header[14] = samples >> 47;
488 sox_header[15] = samples >> 55;
489 sout.seekp(0, std::ios::beg);
490 if(!sout)
491 throw std::runtime_error("Can't seek to fix .sox header");
492 sout.write(reinterpret_cast<char*>(sox_header), 32);
493 if(!sout)
494 throw std::runtime_error("Can't fix audio header");
495 std::cout << "Sound sampling rate is " << static_cast<double>(apurate) / 768.0 << "Hz" << std::endl;
496 std::cout << "Wrote " << samples << " samples." << std::endl;
497 std::cout << "Audio length is " << 768.0 * samples / apurate << "s." << std::endl;
498 double vrate = 0;
499 double vrate2 = 0;
500 if(is_pal)
501 vrate2 = 425568.0;
502 else if(flags & FLAG_FRAMERATE)
503 vrate2 = 357368.0;
504 else
505 vrate2 = 357366.0;
506 vrate = cpurate / vrate2;
507 std::cout << "Video frame rate is " << cpurate << "/" << vrate2 << "Hz" << std::endl;
508 std::cout << "Wrote " << frames << " frames." << std::endl;
509 std::cout << "Video length is " << frames / vrate << "s." << std::endl;
512 void syntax()
514 std::cerr << "Syntax: sdump2sox [<options>] <input-file> <yuv-output-file> <sox-output-file> "
515 << "[<tc-output-file>]" << std::endl;
516 std::cerr << "-W\tDump 512-wide instead of 256-wide." << std::endl;
517 std::cerr << "-H\tDump 448/480-high instead of 224/240-high." << std::endl;
518 std::cerr << "-D\tDedup the output (also uses exact timecodes)." << std::endl;
519 std::cerr << "-h\tDump 512x448/480, doing blending for 512x224/240." << std::endl;
520 std::cerr << "-F\tDump at interlaced framerate instead of non-interlaced (no effect if dedup)." << std::endl;
521 std::cerr << "-f\tDump using full range instead of TV range." << std::endl;
522 std::cerr << "-7\tDump using ITU.709 instead of ITU.601." << std::endl;
523 std::cerr << "-2\tDump using SMPTE-240M instead of ITU.601." << std::endl;
524 std::cerr << "-8\tDump using 8 bits instead of 16 bits." << std::endl;
527 int main(int argc, char** argv)
529 if(argc < 4) {
530 syntax();
531 return 1;
533 uint32_t flags = 0;
534 uint32_t idx1 = 0;
535 uint32_t idx2 = 0;
536 uint32_t idx3 = 0;
537 uint32_t idx4 = 0;
538 for(unsigned i = 1; i < argc; i++) {
539 if(argv[i][0] == '-')
540 for(unsigned j = 1; argv[i][j]; j++)
541 switch(argv[i][j]) {
542 case 'W':
543 flags |= FLAG_WIDTH;
544 break;
545 case 'H':
546 flags |= FLAG_HEIGHT;
547 break;
548 case 'F':
549 flags |= FLAG_FRAMERATE;
550 break;
551 case 'D':
552 flags |= FLAG_DEDUP;
553 break;
554 case 'f':
555 flags |= FLAG_FULLRANGE;
556 break;
557 case 'h':
558 flags |= (FLAG_FAKENLARGE | FLAG_WIDTH | FLAG_HEIGHT);
559 break;
560 case '7':
561 if(flags & FLAG_CS_MASK) {
562 syntax();
563 return 1;
565 flags |= FLAG_ITU709;
566 break;
567 case '2':
568 if(flags & FLAG_CS_MASK) {
569 syntax();
570 return 1;
572 flags |= FLAG_SMPTE240M;
573 break;
574 case '8':
575 flags |= FLAG_8BIT;
576 break;
577 default:
578 syntax();
579 return 1;
581 else if(!idx1)
582 idx1 = i;
583 else if(!idx2)
584 idx2 = i;
585 else if(!idx3)
586 idx3 = i;
587 else if(!idx4)
588 idx4 = i;
589 else {
590 syntax();
591 return 1;
594 if(idx4 && !(flags & FLAG_DEDUP)) {
595 syntax();
596 return 1;
598 std::ifstream in(argv[idx1], std::ios::in | std::ios::binary);
599 if(!in) {
600 std::cerr << "Error: Can't open '" << argv[idx1] << "'" << std::endl;
601 return 2;
603 std::ofstream yout(argv[idx2], std::ios::out | std::ios::binary);
604 if(!yout) {
605 std::cerr << "Error: Can't open '" << argv[idx2] << "'" << std::endl;
606 return 2;
608 std::ofstream sout(argv[idx3], std::ios::out | std::ios::binary);
609 if(!sout) {
610 std::cerr << "Error: Can't open '" << argv[idx3] << "'" << std::endl;
611 return 2;
613 std::ofstream tout;
614 if(flags & FLAG_DEDUP) {
615 if(idx4)
616 tout.open(argv[idx4], std::ios::out);
617 else
618 tout.open(argv[idx2] + std::string(".tc"), std::ios::out);
619 if(!tout) {
620 std::cerr << "Error: Can't open '" << argv[idx2] << ".tc'" << std::endl;
621 return 2;
624 try {
625 sdump2sox(in, yout, sout, tout, flags);
626 in.close();
627 yout.close();
628 sout.close();
629 tout.close();
630 } catch(std::exception& e) {
631 std::cerr << "Error: " << e.what() << std::endl;
632 in.close();
633 yout.close();
634 sout.close();
635 tout.close();
636 return 3;
638 return 0;