/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #ifndef GDCMIMPLICITDATAELEMENT_TXX #define GDCMIMPLICITDATAELEMENT_TXX #include "gdcmSequenceOfItems.h" #include "gdcmValueIO.h" #include "gdcmSwapper.h" #ifdef GDCM_WORDS_BIGENDIAN #include "gdcmTagToVR.h" #endif namespace gdcm_ns { //----------------------------------------------------------------------------- template std::istream &ImplicitDataElement::Read(std::istream &is) { ReadPreValue(is); return ReadValue(is); } template std::istream &ImplicitDataElement::ReadPreValue(std::istream& is) { TagField.Read(is); // See PS 3.5, 7.1.3 Data Element Structure With Implicit VR // Read Tag if( !is ) { if( !is.eof() ) // FIXME This should not be needed assert(0 && "Should not happen"); return is; } const Tag itemStartItem(0xfffe,0xe000); if( TagField == itemStartItem ) return is; //assert( TagField != Tag(0xfffe,0xe0dd) ); // Read Value Length if( !ValueLengthField.Read(is) ) { //assert(0 && "Should not happen"); throw Exception("Impossible ValueLengthField"); return is; } return is; } template std::istream &ImplicitDataElement::ReadValue(std::istream &is, bool readvalues) { if( is.eof() ) return is; const Tag itemStartItem(0xfffe,0xe000); assert( TagField != itemStartItem ); /* * technically this should not be needed, but what if an implementor, forgot * to set VL = 0, then we should make sure to exit early */ const Tag itemDelItem(0xfffe,0xe00d); if( TagField == itemDelItem ) { if( ValueLengthField != 0 ) { gdcmWarningMacro( "VL should be set to 0" ); } ValueField = nullptr; return is; } //std::cerr << "imp cur tag=" << TagField << " VL=" << ValueLengthField << std::endl; //if( ValueLengthField > length && !ValueLengthField.IsUndefined() ) // { // gdcmWarningMacro( "Cannot read more length than what is remaining in the file" ); // throw Exception( "Impossible" ); // } if( ValueLengthField == 0 ) { // Simple fast path ValueField = nullptr; return is; } else if( ValueLengthField.IsUndefined() ) { //assert( de.GetVR() == VR::SQ ); // FIXME what if I am reading the pixel data... //assert( TagField != Tag(0x7fe0,0x0010) ); if( TagField != Tag(0x7fe0,0x0010) ) { ValueField = new SequenceOfItems; } else { gdcmErrorMacro( "Undefined value length is impossible in non-encapsulated Transfer Syntax. Proceeding with caution" ); ValueField = new SequenceOfFragments; } //VRField = VR::SQ; } else { if( true /*ValueLengthField < 8 */ ) { ValueField = new ByteValue; } else { // In the following we read 4 more bytes in the Value field // to find out if this is a SQ or not // there is still work to do to handle the PMS featured SQ // where item Start is in fact 0xfeff, 0x00e0 ... sigh const Tag itemStart(0xfffe, 0xe000); #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION const Tag itemPMSStart(0xfeff, 0x00e0); const Tag itemPMSStart2(0x3f3f, 0x3f00); #endif Tag item; // TODO FIXME // This is pretty dumb to actually read to later on seekg back, why not `peek` directly ? item.Read(is); // Maybe this code can later be rewritten as I believe that seek back // is very slow... is.seekg(-4, std::ios::cur ); if( item == itemStart ) { assert( TagField != Tag(0x7fe0,0x0010) ); ValueField = new SequenceOfItems; } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION else if ( item == itemPMSStart ) { // MR_Philips_Intera_No_PrivateSequenceImplicitVR.dcm gdcmWarningMacro( "Illegal Tag for Item starter: " << TagField << " should be: " << itemStart ); // TODO: We READ Explicit ok...but we store Implicit ! // Indeed when copying the VR will be saved... pretty cool eh ? ValueField = new SequenceOfItems; ValueField->SetLength(ValueLengthField); // perform realloc std::streampos start = is.tellg(); try { if( !ValueIO::Read(is,*ValueField,readvalues) ) { assert(0 && "Should not happen"); } gdcmWarningMacro( "Illegal: Explicit SQ found in a file with " "TransferSyntax=Implicit for tag: " << TagField ); } catch( Exception & ) { // MR_ELSCINT1_00e1_1042_SQ_feff_00e0_Item.dcm std::streampos current = is.tellg(); std::streamoff diff = start - current; is.seekg( diff, std::ios::cur ); assert( diff == -14 ); ValueIO::Read(is,*ValueField,readvalues); } catch( std::exception & ) { ValueLengthField = ValueField->GetLength(); } return is; } else if ( item == itemPMSStart2 && false ) { gdcmWarningMacro( "Illegal: SQ start with " << itemPMSStart2 << " instead of " << itemStart << " for tag: " << TagField ); ValueField = new SequenceOfItems; ValueField->SetLength(ValueLengthField); // perform realloc if( !ValueIO::Read(is,*ValueField,readvalues) ) { assert(0 && "Should not happen"); } return is; } #endif else { ValueField = new ByteValue; } } } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION // THE WORST BUG EVER. From GE Workstation if( ValueLengthField == 13 ) { // Historically gdcm did not enforce proper length // thus Theralys started writing illegal DICOM images: const Tag theralys1(0x0008,0x0070); const Tag theralys2(0x0008,0x0080); if( TagField != theralys1 && TagField != theralys2 ) { gdcmWarningMacro( "GE,13: Replacing VL=0x000d with VL=0x000a, for Tag=" << TagField << " in order to read a buggy DICOM file." ); ValueLengthField = 10; } } #endif #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION if( ValueLengthField == 0x31f031c && TagField == Tag(0x031e,0x0324) ) { // TestImages/elbow.pap gdcmWarningMacro( "Replacing a VL. To be able to read a supposively " "broken Payrus file." ); ValueLengthField = 202; // 0xca } #endif // We have the length we should be able to read the value this->SetValueFieldLength( ValueLengthField, readvalues ); bool failed; #ifdef GDCM_WORDS_BIGENDIAN VR vrfield = GetVRFromTag( TagField ); if( vrfield & VR::VRASCII || vrfield == VR::INVALID ) { //assert( VRField.GetSize() == 1 ); failed = !ValueIO::Read(is,*ValueField,readvalues); } else { assert( vrfield & VR::VRBINARY ); unsigned int vrsize = vrfield.GetSize(); assert( vrsize == 1 || vrsize == 2 || vrsize == 4 || vrsize == 8 ); if(vrfield==VR::AT) vrsize = 2; switch(vrsize) { case 1: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 2: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 4: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 8: failed = !ValueIO::Read(is,*ValueField,readvalues); break; default: failed = true; assert(0); } } #else failed = !ValueIO::Read(is,*ValueField,readvalues); #endif if( failed ) { // Special handling for PixelData tag: #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION if( TagField == Tag(0x7fe0,0x0010) ) { gdcmWarningMacro( "Incomplete Pixel Data found, use file at own risk" ); is.clear(); } else #endif /* GDCM_SUPPORT_BROKEN_IMPLEMENTATION */ { throw Exception("Should not happen (imp)"); } return is; } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION // dcmtk 3.5.4 is resilient to broken explicit SQ length and will properly // recompute it as long as each of the Item lengths are correct VL dummy = ValueField->GetLength(); if( ValueLengthField != dummy ) { gdcmWarningMacro( "ValueLengthField was bogus" ); assert(0); ValueLengthField = dummy; } #else assert( ValueLengthField == ValueField->GetLength() ); assert( VRField == VR::INVALID ); #endif return is; } //----------------------------------------------------------------------------- template std::istream &ImplicitDataElement::ReadWithLength(std::istream &is, VL & length, bool readvalues) { ReadPreValue(is); return ReadValueWithLength(is, length, readvalues); } template std::istream &ImplicitDataElement::ReadValueWithLength(std::istream& is, VL & length, bool readvalues) { if( is.eof() ) return is; const Tag itemStartItem(0xfffe,0xe000); if( TagField == itemStartItem ) return is; /* * technically this should not be needed, but what if an implementor, forgot * to set VL = 0, then we should make sure to exit early */ const Tag itemDelItem(0xfffe,0xe00d); if( TagField == itemDelItem ) { if( ValueLengthField != 0 ) { gdcmWarningMacro( "VL should be set to 0" ); } ValueField = nullptr; return is; } //std::cerr << "imp cur tag=" << TagField << " VL=" << ValueLengthField << std::endl; if( ValueLengthField > length && !ValueLengthField.IsUndefined() ) { gdcmWarningMacro( "Cannot read more length than what is remaining in the file" ); throw Exception( "Impossible (more)" ); } if( ValueLengthField == 0 ) { // Simple fast path ValueField = nullptr; return is; } else if( ValueLengthField.IsUndefined() ) { //assert( de.GetVR() == VR::SQ ); // FIXME what if I am reading the pixel data... //assert( TagField != Tag(0x7fe0,0x0010) ); if( TagField != Tag(0x7fe0,0x0010) ) { ValueField = new SequenceOfItems; } else { gdcmErrorMacro( "Undefined value length is impossible in non-encapsulated Transfer Syntax. Proceeding with caution" ); ValueField = new SequenceOfFragments; } //VRField = VR::SQ; } else { if( true /*ValueLengthField < 8*/ ) { ValueField = new ByteValue; } else { // In the following we read 4 more bytes in the Value field // to find out if this is a SQ or not // there is still work to do to handle the PMS featured SQ // where item Start is in fact 0xfeff, 0x00e0 ... sigh const Tag itemStart(0xfffe, 0xe000); #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION const Tag itemPMSStart(0xfeff, 0x00e0); const Tag itemPMSStart2(0x3f3f, 0x3f00); #endif Tag item; // TODO FIXME // This is pretty dumb to actually read to later on seekg back, why not `peek` directly ? item.Read(is); // Maybe this code can later be rewritten as I believe that seek back // is very slow... is.seekg(-4, std::ios::cur ); if( item == itemStart ) { assert( TagField != Tag(0x7fe0,0x0010) ); ValueField = new SequenceOfItems; } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION else if ( item == itemPMSStart ) { // MR_Philips_Intera_No_PrivateSequenceImplicitVR.dcm gdcmWarningMacro( "Illegal Tag for Item starter: " << TagField << " should be: " << itemStart ); // TODO: We READ Explicit ok...but we store Implicit ! // Indeed when copying the VR will be saved... pretty cool eh ? ValueField = new SequenceOfItems; ValueField->SetLength(ValueLengthField); // perform realloc std::streampos start = is.tellg(); try { if( !ValueIO::Read(is,*ValueField,readvalues) ) { assert(0 && "Should not happen"); } gdcmWarningMacro( "Illegal: Explicit SQ found in a file with " "TransferSyntax=Implicit for tag: " << TagField ); } catch( Exception &) { // MR_ELSCINT1_00e1_1042_SQ_feff_00e0_Item.dcm std::streampos current = is.tellg(); std::streamoff diff = start - current;//could be bad, if the specific implementation does not support negative streamoff values. is.seekg( diff, std::ios::cur ); assert( diff == -14 ); ValueIO::Read(is,*ValueField,readvalues); } catch( std::exception & ) { ValueLengthField = ValueField->GetLength(); } return is; } else if ( item == itemPMSStart2 ) { assert( 0 ); // FIXME: Sync Read/ReadWithLength gdcmWarningMacro( "Illegal: SQ start with " << itemPMSStart2 << " instead of " << itemStart << " for tag: " << TagField ); ValueField = new SequenceOfItems; ValueField->SetLength(ValueLengthField); // perform realloc if( !ValueIO::Read(is,*ValueField,readvalues) ) { assert(0 && "Should not happen"); } return is; } #endif else { ValueField = new ByteValue; } } } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION // THE WORST BUG EVER. From GE Workstation if( ValueLengthField == 13 ) { // Historically gdcm did not enforce proper length // thus Theralys started writing illegal DICOM images: const Tag theralys1(0x0008,0x0070); const Tag theralys2(0x0008,0x0080); if( TagField != theralys1 && TagField != theralys2 ) { gdcmWarningMacro( "GE,13: Replacing VL=0x000d with VL=0x000a, for Tag=" << TagField << " in order to read a buggy DICOM file." ); ValueLengthField = 10; } } #endif #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION if( ValueLengthField == 0x31f031c && TagField == Tag(0x031e,0x0324) ) { // TestImages/elbow.pap gdcmWarningMacro( "Replacing a VL. To be able to read a supposively" "broken Payrus file." ); ValueLengthField = 202; // 0xca } #endif // We have the length we should be able to read the value ValueField->SetLength(ValueLengthField); // perform realloc bool failed; #ifdef GDCM_WORDS_BIGENDIAN VR vrfield = GetVRFromTag( TagField ); if( vrfield & VR::VRASCII || vrfield == VR::INVALID ) { //assert( VRField.GetSize() == 1 ); failed = !ValueIO::Read(is,*ValueField,readvalues); } else { assert( vrfield & VR::VRBINARY ); unsigned int vrsize = vrfield.GetSize(); assert( vrsize == 1 || vrsize == 2 || vrsize == 4 || vrsize == 8 ); if(vrfield==VR::AT) vrsize = 2; switch(vrsize) { case 1: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 2: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 4: failed = !ValueIO::Read(is,*ValueField,readvalues); break; case 8: failed = !ValueIO::Read(is,*ValueField,readvalues); break; default: failed = true; assert(0); } } #else failed = !ValueIO::Read(is,*ValueField,readvalues); #endif if( failed ) //if( !ValueIO::Read(is,*ValueField) ) { // Special handling for PixelData tag: #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION if( TagField == Tag(0x7fe0,0x0010) ) { gdcmWarningMacro( "Incomplete Pixel Data found, use file at own risk" ); is.clear(); } else #endif /* GDCM_SUPPORT_BROKEN_IMPLEMENTATION */ { throw Exception("Should not happen (imp)"); } return is; } #ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION // dcmtk 3.5.4 is resilient to broken explicit SQ length and will properly recompute it // as long as each of the Item lengths are correct VL dummy = ValueField->GetLength(); if( ValueLengthField != dummy ) { gdcmWarningMacro( "ValueLengthField was bogus" ); ValueLengthField = dummy; } #else assert( ValueLengthField == ValueField->GetLength() ); assert( VRField == VR::INVALID ); #endif return is; } //----------------------------------------------------------------------------- template const std::ostream &ImplicitDataElement::Write(std::ostream &os) const { // See PS 3.5, 7.1.3 Data Element Structure With Implicit VR // Write Tag if( !TagField.Write(os) ) { assert(0 && "Should not happen"); return os; } // Write Value Length // The following code should always work since we are in the case of IVRLE - CP246 // however broken file such as PET-GE-dicomwrite-PixelDataSQUNv2.dcm breaks this assumption right here: // see bug #502 const SequenceOfItems *sqi = dynamic_cast( ValueField.GetPointer() ); //GetSequenceOfItems(); if( sqi && !ValueLengthField.IsUndefined() ) { // Hum, we might have to recompute the length: // See TestWriter2, where an explicit SQ is converted to implicit SQ VL len = sqi->template ComputeLength(); //assert( len == ValueLengthField ); if( !len.Write(os) ) { assert(0 && "Should not happen"); return os; } } else // It should be safe to simply use the ValueLengthField as stored: { // Do not allow writing file such as: dcm4che_UndefinedValueLengthInImplicitTS.dcm if( TagField == Tag(0x7fe0,0x0010) && ValueLengthField.IsUndefined() ) throw Exception( "VL u/f Impossible" ); if( !ValueLengthField.Write(os) ) { assert(0 && "Should not happen"); return os; } } // Write Value if( ValueLengthField ) { assert( ValueField ); gdcmAssertAlwaysMacro( ValueLengthField == ValueField->GetLength() ); assert( TagField != Tag(0xfffe, 0xe00d) && TagField != Tag(0xfffe, 0xe0dd) ); if( !ValueIO::Write(os,*ValueField) ) { assert(0 && "Should not happen"); return os; } } return os; } } // end namespace gdcm_ns #endif // GDCMIMPLICITDATAELEMENT_TXX