1 /*****************************************************************************/
2 // Copyright 2006-2019 Adobe Systems Incorporated
3 // All Rights Reserved.
4 //
5 // NOTICE: Adobe permits you to use, modify, and distribute this file in
6 // accordance with the terms of the Adobe license agreement accompanying it.
7 /*****************************************************************************/
8
9 // Process exit codes
10 // ------------------
11 //
12 // As usual, 0 indicates success.
13 //
14 // If an exception occurs, the exit code will be equal to:
15 //
16 // DNG SDK error code - 100000 + 100
17 //
18 // For example, the error dng_error_memory, which has a DNG SDK error code of
19 // 100005, is returned as an exit code of 105.
20 //
21 // This convention accounts for the fact that the shell truncates process exit
22 // codes to 8 bits and that the exit code 1 is used by ASAN to signal that a
23 // memory error occurred (so mapping the first DNG SDK error code to an exit
24 // code of 1 would not be a good idea).
25
26 /*****************************************************************************/
27
28 #include "dng_color_space.h"
29 #include "dng_date_time.h"
30 #include "dng_exceptions.h"
31 #include "dng_file_stream.h"
32 #include "dng_globals.h"
33 #include "dng_host.h"
34 #include "dng_ifd.h"
35 #include "dng_image_writer.h"
36 #include "dng_info.h"
37 #include "dng_linearization_info.h"
38 #include "dng_mosaic_info.h"
39 #include "dng_negative.h"
40 #include "dng_preview.h"
41 #include "dng_render.h"
42 #include "dng_simple_image.h"
43 #include "dng_tag_codes.h"
44 #include "dng_tag_types.h"
45 #include "dng_tag_values.h"
46 #include "dng_xmp.h"
47 #include "dng_xmp_sdk.h"
48
49 /*****************************************************************************/
50
51 #if qDNGValidateTarget
52
53 /*****************************************************************************/
54
55 #define kDNGValidateVersion "1.5"
56
57 /*****************************************************************************/
58
59 static bool gFourColorBayer = false;
60
61 static int32 gMosaicPlane = -1;
62
63 static bool gIgnoreEnhanced = false;
64
65 static uint32 gPreferredSize = 0;
66 static uint32 gMinimumSize = 0;
67 static uint32 gMaximumSize = 0;
68
69 static uint32 gProxyDNGSize = 0;
70
71 static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get ();
72
73 static uint32 gFinalPixelType = ttByte;
74
75 static dng_string gDumpStage1;
76 static dng_string gDumpStage2;
77 static dng_string gDumpStage3;
78 static dng_string gDumpTransparency;
79 static dng_string gDumpDepthMap;
80 static dng_string gDumpTIF;
81 static dng_string gDumpDNG;
82
83 /*****************************************************************************/
84
dng_validate(const char * filename)85 static dng_error_code dng_validate (const char *filename)
86 {
87
88 printf ("Validating \"%s\"...\n", filename);
89
90 try
91 {
92
93 dng_file_stream stream (filename);
94
95 dng_host host;
96
97 host.SetPreferredSize (gPreferredSize);
98 host.SetMinimumSize (gMinimumSize );
99 host.SetMaximumSize (gMaximumSize );
100
101 host.ValidateSizes ();
102
103 if (host.MinimumSize ())
104 {
105
106 host.SetForPreview (true);
107
108 gDumpDNG.Clear ();
109
110 }
111
112 if (gDumpDNG.NotEmpty ())
113 {
114
115 host.SetSaveDNGVersion (dngVersion_SaveDefault);
116
117 host.SetSaveLinearDNG (false);
118
119 host.SetKeepOriginalFile (false);
120
121 }
122
123 // Read into the negative.
124
125 AutoPtr<dng_negative> negative;
126
127 {
128
129 dng_info info;
130
131 info.Parse (host, stream);
132
133 info.PostParse (host);
134
135 if (!info.IsValidDNG ())
136 {
137 return dng_error_bad_format;
138 }
139
140 negative.Reset (host.Make_dng_negative ());
141
142 negative->Parse (host, stream, info);
143
144 negative->PostParse (host, stream, info);
145
146 if (info.fEnhancedIndex != -1 && !gIgnoreEnhanced)
147 {
148
149 dng_timer timer ("Read enhanced image time");
150
151 negative->ReadEnhancedImage (host, stream, info);
152
153 }
154
155 else
156 {
157
158 dng_timer timer ("Raw image read time");
159
160 negative->ReadStage1Image (host, stream, info);
161
162 }
163
164 if (info.fMaskIndex != -1)
165 {
166
167 dng_timer timer ("Transparency mask read time");
168
169 negative->ReadTransparencyMask (host, stream, info);
170
171 }
172
173 if (info.fDepthIndex != -1)
174 {
175
176 dng_timer timer ("Depth map read time");
177
178 negative->ReadDepthMap (host, stream, info);
179
180 }
181
182 negative->ValidateRawImageDigest (host);
183
184 }
185
186 // Option to write stage 1 image.
187
188 if (gDumpStage1.NotEmpty ())
189 {
190
191 if (negative->Stage1Image ())
192 {
193
194 dng_file_stream stream2 (gDumpStage1.Get (), true);
195
196 const dng_image &stage1 = *negative->Stage1Image ();
197
198 dng_image_writer writer;
199
200 writer.WriteTIFF (host,
201 stream2,
202 stage1,
203 stage1.Planes () >= 3 ? piRGB
204 : piBlackIsZero);
205
206 }
207
208 gDumpStage1.Clear ();
209
210 }
211
212 // Metadata.
213
214 negative->SynchronizeMetadata ();
215
216 // Build stage 2 image.
217
218 if (negative->Stage1Image ())
219 {
220
221 dng_timer timer ("Linearization time");
222
223 negative->BuildStage2Image (host);
224
225 }
226
227 if (gDumpStage2.NotEmpty ())
228 {
229
230 dng_file_stream stream2 (gDumpStage2.Get (), true);
231
232 if (negative->Stage2Image ())
233 {
234
235 const dng_image &stage2 = *negative->Stage2Image ();
236
237 dng_image_writer writer;
238
239 writer.WriteTIFF (host,
240 stream2,
241 stage2,
242 stage2.Planes () >= 3 ? piRGB
243 : piBlackIsZero);
244
245 }
246
247 gDumpStage2.Clear ();
248
249 }
250
251 // Four color Bayer option.
252
253 if (gFourColorBayer)
254 {
255 negative->SetFourColorBayer ();
256 }
257
258 // Build stage 3 image.
259
260 if (negative->Stage2Image ())
261 {
262
263 dng_timer timer ("Interpolate time");
264
265 negative->BuildStage3Image (host,
266 gMosaicPlane);
267
268 }
269
270 else
271 {
272
273 negative->ResizeTransparencyToMatchStage3 (host);
274
275 negative->ResizeDepthToMatchStage3 (host);
276
277 }
278
279 // Convert to proxy, if requested.
280
281 if (gProxyDNGSize)
282 {
283
284 dng_timer timer ("ConvertToProxy time");
285
286 dng_image_writer writer;
287
288 negative->ConvertToProxy (host,
289 writer,
290 gProxyDNGSize);
291
292 }
293
294 // Flatten transparency, if required.
295
296 if (negative->NeedFlattenTransparency (host))
297 {
298
299 dng_timer timer ("FlattenTransparency time");
300
301 negative->FlattenTransparency (host);
302
303 }
304
305 if (gDumpStage3.NotEmpty ())
306 {
307
308 dng_file_stream stream2 (gDumpStage3.Get (), true);
309
310 const dng_image &stage3 = *negative->Stage3Image ();
311
312 dng_image_writer writer;
313
314 writer.WriteTIFF (host,
315 stream2,
316 stage3,
317 stage3.Planes () >= 3 ? piRGB
318 : piBlackIsZero);
319
320 gDumpStage3.Clear ();
321
322 }
323
324 if (gDumpTransparency.NotEmpty ())
325 {
326
327 if (negative->TransparencyMask ())
328 {
329
330 dng_file_stream stream2 (gDumpTransparency.Get (), true);
331
332 const dng_image &transparencyMask = *negative->TransparencyMask ();
333
334 dng_image_writer writer;
335
336 writer.WriteTIFF (host,
337 stream2,
338 transparencyMask,
339 piBlackIsZero);
340
341 }
342
343 gDumpTransparency.Clear ();
344
345 }
346
347 if (gDumpDepthMap.NotEmpty ())
348 {
349
350 if (negative->HasDepthMap ())
351 {
352
353 dng_file_stream stream2 (gDumpDepthMap.Get (), true);
354
355 const dng_image &depthMap = *negative->DepthMap ();
356
357 dng_image_writer writer;
358
359 writer.WriteTIFF (host,
360 stream2,
361 depthMap,
362 piBlackIsZero);
363
364 }
365
366 gDumpDepthMap.Clear ();
367
368 }
369
370 // Output DNG file if requested.
371
372 if (gDumpDNG.NotEmpty ())
373 {
374
375 // Build the preview list.
376
377 dng_preview_list previewList;
378
379 dng_date_time_info dateTimeInfo;
380
381 CurrentDateTimeAndZone (dateTimeInfo);
382
383 for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++)
384 {
385
386 // Skip preview if writing a compresssed main image to save space
387 // in this example code.
388
389 if (negative->RawJPEGImage () != NULL && previewIndex > 0)
390 {
391 break;
392 }
393
394 // Report timing.
395
396 dng_timer timer (previewIndex == 0 ? "Build thumbnail time"
397 : "Build preview time");
398
399 // Render a preview sized image.
400
401 AutoPtr<dng_image> previewImage;
402
403 {
404
405 dng_render render (host, *negative);
406
407 render.SetFinalSpace (negative->IsMonochrome () ? dng_space_GrayGamma22::Get ()
408 : dng_space_sRGB ::Get ());
409
410 render.SetFinalPixelType (ttByte);
411
412 render.SetMaximumSize (previewIndex == 0 ? 256 : 1024);
413
414 previewImage.Reset (render.Render ());
415
416 }
417
418 // Don't write the preview if it is same size as thumbnail.
419
420 if (previewIndex > 0 &&
421 Max_uint32 (previewImage->Bounds ().W (),
422 previewImage->Bounds ().H ()) <= 256)
423 {
424 break;
425 }
426
427 // If we have compressed JPEG data, create a compressed thumbnail. Otherwise
428 // save a uncompressed thumbnail.
429
430 bool useCompressedPreview = (negative->RawJPEGImage () != NULL) ||
431 (previewIndex > 0);
432
433 AutoPtr<dng_preview> preview (useCompressedPreview ?
434 (dng_preview *) new dng_jpeg_preview :
435 (dng_preview *) new dng_image_preview);
436
437 // Setup up preview info.
438
439 preview->fInfo.fApplicationName .Set ("dng_validate");
440 preview->fInfo.fApplicationVersion.Set (kDNGValidateVersion);
441
442 preview->fInfo.fSettingsName.Set ("Default");
443
444 preview->fInfo.fColorSpace = previewImage->Planes () == 1 ?
445 previewColorSpace_GrayGamma22 :
446 previewColorSpace_sRGB;
447
448 preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601 ();
449
450 if (!useCompressedPreview)
451 {
452
453 dng_image_preview *imagePreview = dynamic_cast<dng_image_preview *> (preview.Get ());
454
455 imagePreview->fImage.Reset (previewImage.Release ());
456
457 }
458
459 else
460 {
461
462 dng_jpeg_preview *jpegPreview = dynamic_cast<dng_jpeg_preview *> (preview.Get ());
463
464 int32 quality = (previewIndex == 0 ? 8 : 5);
465
466 dng_image_writer writer;
467
468 writer.EncodeJPEGPreview (host,
469 *previewImage,
470 *jpegPreview,
471 quality);
472
473 }
474
475 previewList.Append (preview);
476
477 }
478
479 // Write DNG file.
480
481 dng_file_stream stream2 (gDumpDNG.Get (), true);
482
483 {
484
485 dng_timer timer ("Write DNG time");
486
487 dng_image_writer writer;
488
489 writer.WriteDNG (host,
490 stream2,
491 *negative.Get (),
492 &previewList,
493 dngVersion_Current,
494 false);
495
496 }
497
498 gDumpDNG.Clear ();
499
500 }
501
502 // Output TIF file if requested.
503
504 if (gDumpTIF.NotEmpty ())
505 {
506
507 // Render final image.
508
509 dng_render render (host, *negative);
510
511 render.SetFinalSpace (*gFinalSpace );
512 render.SetFinalPixelType (gFinalPixelType);
513
514 if (host.MinimumSize ())
515 {
516
517 dng_point stage3Size = negative->Stage3Image ()->Size ();
518
519 render.SetMaximumSize (Max_uint32 (stage3Size.v,
520 stage3Size.h));
521
522 }
523
524 AutoPtr<dng_image> finalImage;
525
526 {
527
528 dng_timer timer ("Render time");
529
530 finalImage.Reset (render.Render ());
531
532 }
533
534 finalImage->Rotate (negative->Orientation ());
535
536 // Now that Camera Raw supports non-raw formats, we should
537 // not keep any Camera Raw settings in the XMP around when
538 // writing rendered files.
539
540 if (negative->GetXMP ())
541 {
542
543 negative->GetXMP ()->RemoveProperties (XMP_NS_CRS);
544 negative->GetXMP ()->RemoveProperties (XMP_NS_CRSS);
545 negative->GetXMP ()->RemoveProperties (XMP_NS_CRD);
546
547 }
548
549 // Write TIF file.
550
551 dng_file_stream stream2 (gDumpTIF.Get (), true);
552
553 {
554
555 dng_timer timer ("Write TIFF time");
556
557 dng_image_writer writer;
558
559 writer.WriteTIFF (host,
560 stream2,
561 *finalImage.Get (),
562 finalImage->Planes () >= 3 ? piRGB
563 : piBlackIsZero,
564 ccUncompressed,
565 negative.Get (),
566 &render.FinalSpace ());
567
568 }
569
570 gDumpTIF.Clear ();
571
572 }
573
574 }
575
576 catch (const dng_exception &except)
577 {
578
579 return except.ErrorCode ();
580
581 }
582
583 catch (...)
584 {
585
586 return dng_error_unknown;
587
588 }
589
590 printf ("Validation complete\n");
591
592 return dng_error_none;
593
594 }
595
596 /*****************************************************************************/
597
main(int argc,char * argv[])598 int main (int argc, char *argv [])
599 {
600
601 try
602 {
603
604 if (argc == 1)
605 {
606
607 fprintf (stderr,
608 "\n"
609 "dng_validate, version " kDNGValidateVersion " "
610 #if qDNG64Bit
611 "(64-bit)"
612 #else
613 "(32-bit)"
614 #endif
615 "\n"
616 "Copyright 2005-2019 Adobe Systems, Inc.\n"
617 "\n"
618 "Usage: %s [options] file1 file2 ...\n"
619 "\n"
620 "Valid options:\n"
621 "-v Verbose mode\n"
622 "-d <num> Dump line limit (implies -v)\n"
623 "-b4 Use four-color Bayer interpolation\n"
624 "-s <num> Use this sample of multi-sample CFAs\n"
625 "-ignoreEnhanced Ignore the enhanced image IFD\n"
626 "-size <num> Preferred preview image size\n"
627 "-min <num> Minimum preview image size\n"
628 "-max <num> Maximum preview image size\n"
629 "-proxy <num> Target size for proxy DNG\n"
630 "-cs1 Color space: \"sRGB\" (default)\n"
631 "-cs2 Color space: \"Adobe RGB\"\n"
632 "-cs3 Color space: \"ProPhoto RGB\"\n"
633 "-cs4 Color space: \"ColorMatch RGB\"\n"
634 "-cs5 Color space: \"Gray Gamma 1.8\"\n"
635 "-cs6 Color space: \"Gray Gamma 2.2\"\n"
636 "-16 16-bits/channel output\n"
637 "-1 <file> Write stage 1 image to \"<file>.tif\"\n"
638 "-2 <file> Write stage 2 image to \"<file>.tif\"\n"
639 "-3 <file> Write stage 3 image to \"<file>.tif\"\n"
640 "-transparency <file> Write transparency mask to \"<file>.tif\"\n"
641 "-depthMap <file> Write depth map to \"<file>.tif\"\n"
642 "-tif <file> Write TIF image to \"<file>.tif\"\n"
643 "-dng <file> Write DNG image to \"<file>.dng\"\n"
644 "\n",
645 argv [0]);
646
647 return 1;
648
649 }
650
651 int index;
652
653 for (index = 1; index < argc && argv [index] [0] == '-'; index++)
654 {
655
656 dng_string option;
657
658 option.Set (&argv [index] [1]);
659
660 if (option.Matches ("v", true))
661 {
662 gVerbose = true;
663 }
664
665 else if (option.Matches ("d", true))
666 {
667
668 gVerbose = true;
669
670 gDumpLineLimit = 0;
671
672 if (index + 1 < argc)
673 {
674 gDumpLineLimit = atoi (argv [++index]);
675 }
676
677 if (!gDumpLineLimit)
678 {
679 fprintf (stderr, "*** Invalid number after -d\n");
680 return 1;
681 }
682
683 }
684
685 else if (option.Matches ("s", true))
686 {
687
688 if (index + 1 < argc)
689 {
690 gMosaicPlane = atoi (argv [++index]);
691 }
692
693 else
694 {
695 fprintf (stderr, "*** Missing number after -s\n");
696 return 1;
697 }
698
699 }
700
701 else if (option.Matches ("b4", true))
702 {
703 gFourColorBayer = true;
704 }
705
706 else if (option.Matches ("ignoreEnhanced", true))
707 {
708 gIgnoreEnhanced = true;
709 }
710
711 else if (option.Matches ("size", true))
712 {
713
714 if (index + 1 < argc)
715 {
716 gPreferredSize = (uint32) atoi (argv [++index]);
717 }
718
719 else
720 {
721 fprintf (stderr, "*** Missing number after -size\n");
722 return 1;
723 }
724
725 }
726
727 else if (option.Matches ("min", true))
728 {
729
730 if (index + 1 < argc)
731 {
732 gMinimumSize = (uint32) atoi (argv [++index]);
733 }
734
735 else
736 {
737 fprintf (stderr, "*** Missing number after -min\n");
738 return 1;
739 }
740
741 }
742
743 else if (option.Matches ("max", true))
744 {
745
746 if (index + 1 < argc)
747 {
748 gMaximumSize = (uint32) atoi (argv [++index]);
749 }
750
751 else
752 {
753 fprintf (stderr, "*** Missing number after -max\n");
754 return 1;
755 }
756
757 }
758
759 else if (option.Matches ("proxy", true))
760 {
761
762 if (index + 1 < argc)
763 {
764 gProxyDNGSize = (uint32) atoi (argv [++index]);
765 }
766
767 else
768 {
769 fprintf (stderr, "*** Missing number after -proxy\n");
770 return 1;
771 }
772
773 }
774
775 else if (option.Matches ("cs1", true))
776 {
777
778 gFinalSpace = &dng_space_sRGB::Get ();
779
780 }
781
782 else if (option.Matches ("cs2", true))
783 {
784
785 gFinalSpace = &dng_space_AdobeRGB::Get ();
786
787 }
788
789 else if (option.Matches ("cs3", true))
790 {
791
792 gFinalSpace = &dng_space_ProPhoto::Get ();
793
794 }
795
796 else if (option.Matches ("cs4", true))
797 {
798
799 gFinalSpace = &dng_space_ColorMatch::Get ();
800
801 }
802
803 else if (option.Matches ("cs5", true))
804 {
805
806 gFinalSpace = &dng_space_GrayGamma18::Get ();
807
808 }
809
810 else if (option.Matches ("cs6", true))
811 {
812
813 gFinalSpace = &dng_space_GrayGamma22::Get ();
814
815 }
816
817 else if (option.Matches ("16"))
818 {
819
820 gFinalPixelType = ttShort;
821
822 }
823
824 else if (option.Matches ("1"))
825 {
826
827 gDumpStage1.Clear ();
828
829 if (index + 1 < argc)
830 {
831 gDumpStage1.Set (argv [++index]);
832 }
833
834 if (gDumpStage1.IsEmpty () || gDumpStage1.StartsWith ("-"))
835 {
836 fprintf (stderr, "*** Missing file name after -1\n");
837 return 1;
838 }
839
840 if (!gDumpStage1.EndsWith (".tif"))
841 {
842 gDumpStage1.Append (".tif");
843 }
844
845 }
846
847 else if (option.Matches ("2"))
848 {
849
850 gDumpStage2.Clear ();
851
852 if (index + 1 < argc)
853 {
854 gDumpStage2.Set (argv [++index]);
855 }
856
857 if (gDumpStage2.IsEmpty () || gDumpStage2.StartsWith ("-"))
858 {
859 fprintf (stderr, "*** Missing file name after -2\n");
860 return 1;
861 }
862
863 if (!gDumpStage2.EndsWith (".tif"))
864 {
865 gDumpStage2.Append (".tif");
866 }
867
868 }
869
870 else if (option.Matches ("3"))
871 {
872
873 gDumpStage3.Clear ();
874
875 if (index + 1 < argc)
876 {
877 gDumpStage3.Set (argv [++index]);
878 }
879
880 if (gDumpStage3.IsEmpty () || gDumpStage3.StartsWith ("-"))
881 {
882 fprintf (stderr, "*** Missing file name after -3\n");
883 return 1;
884 }
885
886 if (!gDumpStage3.EndsWith (".tif"))
887 {
888 gDumpStage3.Append (".tif");
889 }
890
891 }
892
893 else if (option.Matches ("transparency"))
894 {
895
896 gDumpTransparency.Clear ();
897
898 if (index + 1 < argc)
899 {
900 gDumpTransparency.Set (argv [++index]);
901 }
902
903 if (gDumpTransparency.IsEmpty () || gDumpTransparency.StartsWith ("-"))
904 {
905 fprintf (stderr, "*** Missing file name after -transparency\n");
906 return 1;
907 }
908
909 if (!gDumpTransparency.EndsWith (".tif"))
910 {
911 gDumpTransparency.Append (".tif");
912 }
913
914 }
915
916 else if (option.Matches ("depthMap"))
917 {
918
919 gDumpDepthMap.Clear ();
920
921 if (index + 1 < argc)
922 {
923 gDumpDepthMap.Set (argv [++index]);
924 }
925
926 if (gDumpDepthMap.IsEmpty () || gDumpDepthMap.StartsWith ("-"))
927 {
928 fprintf (stderr, "*** Missing file name after -depthMap\n");
929 return 1;
930 }
931
932 if (!gDumpDepthMap.EndsWith (".tif"))
933 {
934 gDumpDepthMap.Append (".tif");
935 }
936
937 }
938
939 else if (option.Matches ("tif", true))
940 {
941
942 gDumpTIF.Clear ();
943
944 if (index + 1 < argc)
945 {
946 gDumpTIF.Set (argv [++index]);
947 }
948
949 if (gDumpTIF.IsEmpty () || gDumpTIF.StartsWith ("-"))
950 {
951 fprintf (stderr, "*** Missing file name after -tif\n");
952 return 1;
953 }
954
955 if (!gDumpTIF.EndsWith (".tif"))
956 {
957 gDumpTIF.Append (".tif");
958 }
959
960 }
961
962 else if (option.Matches ("dng", true))
963 {
964
965 gDumpDNG.Clear ();
966
967 if (index + 1 < argc)
968 {
969 gDumpDNG.Set (argv [++index]);
970 }
971
972 if (gDumpDNG.IsEmpty () || gDumpDNG.StartsWith ("-"))
973 {
974 fprintf (stderr, "*** Missing file name after -dng\n");
975 return 1;
976 }
977
978 if (!gDumpDNG.EndsWith (".dng"))
979 {
980 gDumpDNG.Append (".dng");
981 }
982
983 }
984
985 else
986 {
987 fprintf (stderr, "*** Unknown option \"-%s\"\n", option.Get ());
988 return 1;
989 }
990
991 }
992
993 if (index == argc)
994 {
995 fprintf (stderr, "*** No file specified\n");
996 return 1;
997 }
998
999 dng_xmp_sdk::InitializeSDK ();
1000
1001 int result = 0;
1002
1003 while (index < argc)
1004 {
1005
1006 dng_error_code error_code = dng_validate (argv [index++]);
1007
1008 if (error_code != dng_error_none)
1009 {
1010
1011 result = error_code - dng_error_unknown + 100;
1012
1013 }
1014
1015 }
1016
1017 dng_xmp_sdk::TerminateSDK ();
1018
1019 return result;
1020
1021 }
1022
1023 catch (...)
1024 {
1025
1026 }
1027
1028 fprintf (stderr, "*** Exception thrown in main routine\n");
1029
1030 return 1;
1031
1032 }
1033
1034 /*****************************************************************************/
1035
1036 #endif
1037
1038 /*****************************************************************************/
1039