/* Copyright (c) MediaArea.net SARL. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the License.html file in the root of the source tree.
*/
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_EIA608_YES)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Text/File_Eia608.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#if MEDIAINFO_EVENTS
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#include "MediaInfo/MediaInfo_Events_Internal.h"
#endif //MEDIAINFO_EVENTS
using namespace std;
//---------------------------------------------------------------------------
//***************************************************************************
// Constants
//***************************************************************************
//---------------------------------------------------------------------------
// CAE-608-E section F.1.1.5
static const int8u Eia608_PAC_Row[]=
{
10,
0, //or 1
2, //or 3
11, //or 12
13, //or 14
4, //or 5
6, //or 7
8 //or 9
};
//***************************************************************************
//
//***************************************************************************
namespace MediaInfoLib
{
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
//---------------------------------------------------------------------------
File_Eia608::File_Eia608()
:File__Analyze()
{
//Configuration
#if MEDIAINFO_EVENTS
ParserIDs[0]=MediaInfo_Parser_Eia608;
StreamIDs_Width[0]=1;
#endif //MEDIAINFO_EVENTS
ParserName="EIA-608";
PTS_DTS_Needed=true;
//In
cc_type=(int8u)-1;
#if MEDIAINFO_EVENTS
MuxingMode=(int8u)-1;
#endif //MEDIAINFO_EVENTS
//Temp
XDS_Level=(size_t)-1;
TextMode=false;
DataChannelMode=false;
cc_data_1_Old=0x00;
cc_data_2_Old=0x00;
HasContent=false;
}
//---------------------------------------------------------------------------
File_Eia608::~File_Eia608()
{
for (size_t Pos=0; Pos<Streams.size(); Pos++)
delete Streams[Pos];
}
//***************************************************************************
// Streams management
//***************************************************************************
//---------------------------------------------------------------------------
void File_Eia608::Streams_Fill()
{
if (Config->File_Eia608_DisplayEmptyStream_Get() && Streams.size()<2)
Streams.resize(2);
if (!HasContent && ServiceDescriptors && ServiceDescriptors->ServiceDescriptors608.find(cc_type)!=ServiceDescriptors->ServiceDescriptors608.end())
{
TextMode=0;
DataChannelMode=0;
Special_14(0x20); //CC1/CC3 fake RCL - Resume Caption Loading
}
for (size_t Pos=0; Pos<Streams.size(); Pos++)
if (Streams[Pos] || (Pos<2 && Config->File_Eia608_DisplayEmptyStream_Get()))
{
Stream_Prepare(Stream_Text);
Fill(Stream_Text, StreamPos_Last, Text_Format, "EIA-608");
Fill(Stream_Text, StreamPos_Last, Text_StreamSize, 0);
Fill(Stream_Text, StreamPos_Last, Text_BitRate_Mode, "CBR");
if (cc_type!=(int8u)-1)
{
string ID=Pos<2?"CC":"T";
ID+='1'+(cc_type*2)+(Pos%2);
Fill(Stream_Text, StreamPos_Last, Text_ID, ID);
Fill(Stream_Text, StreamPos_Last, "CaptionServiceName", ID);
Fill_SetOptions(StreamKind_Last, StreamKind_Last, "CaptionServiceName", "N NT");
}
if (Config->ParseSpeed>=1.0)
{
Fill(Stream_Text, StreamPos_Last, "CaptionServiceContent_IsPresent", DataDetected[Pos+1]?"Yes":"No", Unlimited, true, true); //1 bit per service, starting at 1
Fill_SetOptions(Stream_Text, StreamPos_Last, "CaptionServiceContent_IsPresent", "N NT");
}
if (ServiceDescriptors)
{
servicedescriptors608::iterator ServiceDescriptor=ServiceDescriptors->ServiceDescriptors608.find(cc_type);
if (ServiceDescriptor!=ServiceDescriptors->ServiceDescriptors608.end())
{
if (Pos==0 && Retrieve(Stream_Text, StreamPos_Last, Text_Language).empty()) //Only CC1/CC3
Fill(Stream_Text, StreamPos_Last, Text_Language, ServiceDescriptor->second.language, true);
Fill(Stream_Text, StreamPos_Last, "CaptionServiceDescriptor_IsPresent", "Yes", Unlimited, true, true);
Fill_SetOptions(Stream_Text, StreamPos_Last, "CaptionServiceDescriptor_IsPresent", "N NT");
}
else //ServiceDescriptors pointer is for the support by the transport layer of the info
{
Fill(Stream_Text, StreamPos_Last, "CaptionServiceDescriptor_IsPresent", "No", Unlimited, true, true);
Fill_SetOptions(Stream_Text, StreamPos_Last, "CaptionServiceDescriptor_IsPresent", "N NT");
}
}
}
}
//---------------------------------------------------------------------------
void File_Eia608::Streams_Finish()
{
}
//***************************************************************************
// Buffer - Synchro
//***************************************************************************
//---------------------------------------------------------------------------
void File_Eia608::Read_Buffer_Unsynched()
{
for (size_t StreamPos=0; StreamPos<Streams.size(); StreamPos++)
if (Streams[StreamPos])
{
for (size_t Pos_Y=0; Pos_Y<Streams[StreamPos]->CC_Displayed.size(); Pos_Y++)
{
for (size_t Pos_X=0; Pos_X<Streams[StreamPos]->CC_Displayed[Pos_Y].size(); Pos_X++)
if (Streams[StreamPos])
{
Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Value=L' ';
Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Attribute=0;
if (StreamPos<2)
{
Streams[StreamPos]->CC_NonDisplayed[Pos_Y][Pos_X].Value=L' ';
Streams[StreamPos]->CC_NonDisplayed[Pos_Y][Pos_X].Attribute=0;
}
}
}
Streams[StreamPos]->Synched=false;
}
XDS_Data.clear();
XDS_Level=(size_t)-1;
#if MEDIAINFO_EVENTS
TextMode=true; DataChannelMode=true;
HasChanged();
TextMode=true; DataChannelMode=false;
HasChanged();
TextMode=false; DataChannelMode=true;
HasChanged();
TextMode=false; DataChannelMode=false;
HasChanged();
#endif //MEDIAINFO_EVENTS
}
//***************************************************************************
// Buffer - Global
//***************************************************************************
//---------------------------------------------------------------------------
void File_Eia608::Read_Buffer_Init()
{
if (!IsSub)
{
FrameInfo.DTS=0; //No DTS in container
FrameInfo.PTS=0; //No PTS in container
}
#if MEDIAINFO_DEMUX
if (Frame_Count_NotParsedIncluded==(int64u)-1)
Frame_Count_NotParsedIncluded=Config->Demux_FirstFrameNumber_Get();
if (FrameInfo.DUR==(int64u)-1 && Config->Demux_Rate_Get())
FrameInfo.DUR=float64_int64s(((float64)1000000000)/Config->Demux_Rate_Get());
if (FrameInfo.DTS==(int64u)-1)
FrameInfo.DTS=Config->Demux_FirstDts_Get();
#endif //MEDIAINFO_DEMUX
#if MEDIAINFO_EVENTS
if (MuxingMode==(int8u)-1)
{
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Mpegv && StreamIDs[StreamIDs_Size-3]==0x4741393400000003LL)
MuxingMode=0; //A/53 / DTVCC Transport
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Mpegv && StreamIDs[StreamIDs_Size-3]==0x0000000300000000LL)
MuxingMode=1; //SCTE 20
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Mpegv && StreamIDs[StreamIDs_Size-3]==0x434301F800000000LL)
MuxingMode=2; //DVD-Video
if (StreamIDs_Size>=4 && (ParserIDs[StreamIDs_Size-4]==MediaInfo_Parser_Gxf || ParserIDs[StreamIDs_Size-4]==MediaInfo_Parser_Lxf || ParserIDs[StreamIDs_Size-4]==MediaInfo_Parser_Mxf) && ParserIDs[StreamIDs_Size-2]==MediaInfo_Parser_Cdp)
MuxingMode=3; //Ancillary data / CDP
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Avc)
MuxingMode=4; //SCTE 128 / DTVCC Transport
if (StreamIDs_Size>=2 && ParserIDs[StreamIDs_Size-2]==MediaInfo_Parser_DvDif)
MuxingMode=5; //DV
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Mpeg4 && ParserIDs[StreamIDs_Size-2]==MediaInfo_Parser_Cdp)
MuxingMode=6; //Final Cut / CDP
if (StreamIDs_Size>=2 && ParserIDs[StreamIDs_Size-2]==MediaInfo_Parser_Scc)
MuxingMode=10; //SCC
if (StreamIDs_Size>=3 && ParserIDs[StreamIDs_Size-3]==MediaInfo_Parser_Mpeg4 && ParserIDs[StreamIDs_Size-2]==MediaInfo_Parser_Mpeg4)
MuxingMode=14; //Final Cut / cdat
}
#endif //MEDIAINFO_EVENTS
}
//---------------------------------------------------------------------------
void File_Eia608::Read_Buffer_AfterParsing()
{
Frame_Count++;
Frame_Count_InThisBlock++;
if (Frame_Count_NotParsedIncluded!=(int64u)-1)
Frame_Count_NotParsedIncluded++;
if (FrameInfo.DUR!=(int64u)-1)
{
if (FrameInfo.DTS!=(int64u)-1)
FrameInfo.DTS+=FrameInfo.DUR;
if (FrameInfo.PTS!=(int64u)-1)
FrameInfo.PTS+=FrameInfo.DUR;
}
else
{
FrameInfo.DTS=(int64u)-1;
FrameInfo.PTS=(int64u)-1;
}
}
//---------------------------------------------------------------------------
void File_Eia608::Read_Buffer_Continue()
{
FrameInfo.PTS=FrameInfo.DTS;
if (!Status[IsAccepted])
Accept("EIA-608");
while (Element_Offset+1<Element_Size)
{
int8u cc_data_1, cc_data_2;
Get_B1 (cc_data_1, "cc_data");
Get_B1 (cc_data_2, "cc_data");
//Removing checksume
cc_data_1&=0x7F;
cc_data_2&=0x7F;
//Test if non-printing chars (0x10-0x1F) are repeated (CEA-608-E section D.2)
if (cc_data_1_Old)
{
if (cc_data_1_Old==cc_data_1 && cc_data_2_Old==cc_data_2)
{
//This is duplicate
cc_data_1_Old=0x00;
cc_data_2_Old=0x00;
return; //Nothing to do
}
else if (cc_type==0) // Field 1 only
{
//They should be duplicated, there is a problem
}
cc_data_1_Old=0x00;
cc_data_2_Old=0x00;
}
if ((cc_data_1 && cc_data_1<0x10) || (XDS_Level!=(size_t)-1 && cc_data_1>=0x20)) //XDS
{
XDS(cc_data_1, cc_data_2);
}
else if (cc_data_1>=0x20) //Basic characters
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
Standard(cc_data_1);
if ((cc_data_2&0x7F)>=0x20)
Standard(cc_data_2);
}
else if (cc_data_1) //Special
Special(cc_data_1, cc_data_2);
}
}
//***************************************************************************
// Functions
//***************************************************************************
//---------------------------------------------------------------------------
void File_Eia608::XDS(int8u cc_data_1, int8u cc_data_2)
{
if (cc_data_1 && cc_data_1<0x10 && cc_data_1%2==0)
{
// Continue
cc_data_1--;
for (XDS_Level=0; XDS_Level<XDS_Data.size(); XDS_Level++)
if (XDS_Data[XDS_Level].size()>=2 && XDS_Data[XDS_Level][0]==cc_data_1 && XDS_Data[XDS_Level][1]==cc_data_2)
break;
if (XDS_Level>=XDS_Data.size())
XDS_Level=(size_t)-1; // There is a problem
return;
}
else if (cc_data_1 && cc_data_1<0x0F)
{
// Start
for (XDS_Level=0; XDS_Level<XDS_Data.size(); XDS_Level++)
if (XDS_Data[XDS_Level].size()>=2 && XDS_Data[XDS_Level][0]==cc_data_1 && XDS_Data[XDS_Level][1]==cc_data_2)
break;
if (XDS_Level>=XDS_Data.size())
{
XDS_Level=XDS_Data.size();
XDS_Data.resize(XDS_Level+1);
}
else
XDS_Data[XDS_Level].clear(); // There is a problem, erasing the previous item
}
if (XDS_Level==(size_t)-1)
return; //There is a problem
XDS_Data[XDS_Level].push_back(cc_data_1);
XDS_Data[XDS_Level].push_back(cc_data_2);
if (cc_data_1==0x0F)
XDS();
if (XDS_Level!=(size_t)-1 && XDS_Data[XDS_Level].size()>=36)
XDS_Data[XDS_Level].clear(); // Clear, this is a security
TextMode=0; // This is CC
}
//---------------------------------------------------------------------------
void File_Eia608::XDS()
{
if (XDS_Data[XDS_Level].size()<4)
{
XDS_Data.erase(XDS_Data.begin()+XDS_Level);
XDS_Level=(size_t)-1;
return; //There is a problem
}
switch (XDS_Data[XDS_Level][0])
{
case 0x01 : XDS_Current(); break;
case 0x05 : XDS_Channel(); break;
case 0x09 : XDS_PublicService(); break;
default : ;
}
XDS_Data.erase(XDS_Data.begin()+XDS_Level);
XDS_Level=(size_t)-1;
DataDetected[5]=true; //bit 5=XDS
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Current()
{
switch (XDS_Data[XDS_Level][1])
{
case 0x03 : XDS_Current_ProgramName(); break;
case 0x05 : XDS_Current_ContentAdvisory(); break;
case 0x08 : XDS_Current_CopyAndRedistributionControlPacket(); break;
default : ;
}
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Current_ContentAdvisory()
{
if (XDS_Data[XDS_Level].size()!=6)
{
return; //There is a problem
}
Clear(Stream_General, 0, General_LawRating);
int8u a1a0=(XDS_Data[XDS_Level][2]>>3)&0x3;
const char* ContentAdvisory=NULL;
string ContentDescriptors;
switch (a1a0)
{
case 0:
case 2:
switch (XDS_Data[XDS_Level][2]&0x7) //r2r1r0
{
case 0 : ContentAdvisory="N/A"; break;
case 1 : ContentAdvisory="G"; break;
case 2 : ContentAdvisory="PG"; break;
case 3 : ContentAdvisory="PG-13"; break;
case 4 : ContentAdvisory="R"; break;
case 5 : ContentAdvisory="NC-17"; break;
case 6 : ContentAdvisory="C"; break;
default: ;
}
break;
case 1:
switch (XDS_Data[XDS_Level][3]&0x7) //g2g1g0
{
case 0 : ContentAdvisory="None"; break;
case 1 : ContentAdvisory="TV-Y"; break;
case 2 : ContentAdvisory="TV-Y7"; break;
case 3 : ContentAdvisory="TV-G"; break;
case 4 : ContentAdvisory="TV-PG"; break;
case 5 : ContentAdvisory="TV-14"; break;
case 6 : ContentAdvisory="TV-MA"; break;
case 7 : ContentAdvisory="None"; break;
default: ;
}
if (XDS_Data[XDS_Level][2]&0x20) //Suggestive dialogue
ContentDescriptors+='D';
if (XDS_Data[XDS_Level][3]&0x8) //Coarse language
ContentDescriptors+='L';
if (XDS_Data[XDS_Level][3]&0x10) //Sexual content
ContentDescriptors+='S';
if (XDS_Data[XDS_Level][3]&0x20) //Violence
{
if ((XDS_Data[XDS_Level][3]&0x7)==2) //"TV-Y7" --> Fantasy Violence
ContentDescriptors+="FV";
else
ContentDescriptors+='V';
}
break;
case 3:
if (XDS_Data[XDS_Level][3]&0x8) //a3
{
ContentAdvisory="(Reserved)";
}
else
{
if (XDS_Data[XDS_Level][2]&0x20) //a2
switch (XDS_Data[XDS_Level][3]&0x7) //g2g1g0
{
case 0 : ContentAdvisory="E"; break;
case 1 : ContentAdvisory="G"; break;
case 2 : ContentAdvisory="8+"; break;
case 3 : ContentAdvisory="13+"; break;
case 4 : ContentAdvisory="16+"; break;
case 5 : ContentAdvisory="18+"; break;
default: ;
}
else
switch (XDS_Data[XDS_Level][3]&0x7) //g2g1g0
{
case 0 : ContentAdvisory="E"; break;
case 1 : ContentAdvisory="C"; break;
case 2 : ContentAdvisory="C8+"; break;
case 3 : ContentAdvisory="G"; break;
case 4 : ContentAdvisory="PG"; break;
case 5 : ContentAdvisory="14+"; break;
case 6 : ContentAdvisory="18+"; break;
default: ;
}
}
break;
default: ;
}
if (ContentAdvisory)
{
string ContentAdvisory_String=ContentAdvisory;
if (!ContentDescriptors.empty())
ContentAdvisory_String+=" ("+ContentDescriptors+')';
Fill(Stream_General, 0, General_LawRating, ContentAdvisory_String.c_str());
}
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Current_ProgramName()
{
string ValueS;
for (size_t Pos=2; Pos<XDS_Data[XDS_Level].size()-2; Pos++)
ValueS.append(1, (const char)(XDS_Data[XDS_Level][Pos]));
Ztring Value;
Value.From_UTF8(ValueS.c_str());
Element_Info1(__T("Program Name=")+Value);
if (Retrieve(Stream_General, 0, General_Title).empty())
Fill(Stream_General, 0, General_Title, Value);
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Current_CopyAndRedistributionControlPacket()
{
if (XDS_Data[XDS_Level].size()!=6)
{
return; //There is a problem
}
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Channel()
{
switch (XDS_Data[XDS_Level][1])
{
case 0x01 : XDS_Channel_NetworkName(); break;
default : ;
}
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_Channel_NetworkName()
{
string ValueS;
for (size_t Pos=2; Pos<XDS_Data[XDS_Level].size()-2; Pos++)
ValueS.append(1, (const char)(XDS_Data[XDS_Level][Pos]));
Ztring Value;
Value.From_UTF8(ValueS.c_str());
Element_Info1(__T("Network Name=")+Value);
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_PublicService()
{
switch (XDS_Data[XDS_Level][1])
{
case 0x01 : XDS_PublicService_NationalWeatherService(); break;
default : ;
}
}
//---------------------------------------------------------------------------
void File_Eia608::XDS_PublicService_NationalWeatherService()
{
if (XDS_Data[XDS_Level].size()!=20)
{
return; //There is a problem
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special(int8u cc_data_1, int8u cc_data_2)
{
//Data channel check
DataChannelMode=(cc_data_1&0x08)!=0; //bit3 is the Data Channel number
//Field check
if (cc_type==(int8u)-1)
{
if ((cc_data_1==0x14 || cc_data_1==0x1C) && (cc_data_2&0xF0)==0x20)
cc_type=0;
if ((cc_data_1==0x15 || cc_data_1==0x1D) && (cc_data_2&0xF0)==0x20)
cc_type=1;
}
cc_data_1&=0xF7;
if (cc_data_1==0x15 && (cc_data_2&0xF0)==0x20)
cc_data_1=0x14;
if (cc_data_1>=0x10 && cc_data_1<=0x17 && cc_data_2>=0x40)
{
PreambleAddressCode(cc_data_1, cc_data_2);
}
else
{
switch (cc_data_1)
{
case 0x10 : Special_10(cc_data_2); break;
case 0x11 : Special_11(cc_data_2); break;
case 0x12 : Special_12(cc_data_2); break;
case 0x13 : Special_13(cc_data_2); break;
case 0x14 : Special_14(cc_data_2); break;
case 0x17 : Special_17(cc_data_2); break;
default : Illegal(cc_data_1, cc_data_2);
}
}
//Saving data, for repetition of the code
cc_data_1_Old=cc_data_1;
cc_data_2_Old=cc_data_2;
}
//---------------------------------------------------------------------------
void File_Eia608::PreambleAddressCode(int8u cc_data_1, int8u cc_data_2)
{
//CEA-608-E, Section F.1.1.5
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
Streams[StreamPos]->x=0; //I am not sure of this, specifications are not precise
//Horizontal position
if (!TextMode)
{
Streams[StreamPos]->y=Eia608_PAC_Row[cc_data_1&0x07]+((cc_data_2&0x20)?1:0);
if (Streams[StreamPos]->y>=Eia608_Rows)
{
Streams[StreamPos]->y=Eia608_Rows-1;
}
}
//Attributes (except Underline)
if (cc_data_2&0x10) //0x5x and 0x7x
{
Streams[StreamPos]->x=(((size_t)cc_data_2)&0x0E)<<1;
Streams[StreamPos]->Attribute_Current=Attribute_Color_White;
}
else if ((cc_data_2&0x0E)==0x0E) //0x4E, 0x4F, 0x6E, 0x6F
{
Streams[StreamPos]->Attribute_Current=Attribute_Color_White|Attribute_Italic;
}
else //0x40-0x4D, 0x60-0x6D
Streams[StreamPos]->Attribute_Current=(cc_data_2&0x0E)>>1;
//Underline
if (cc_data_2&0x01)
Streams[StreamPos]->Attribute_Current|=Attribute_Underline;
}
//---------------------------------------------------------------------------
void File_Eia608::Special_10(int8u cc_data_2)
{
switch (cc_data_2)
{
//CEA-608-E, Section 6.2
case 0x20 : break; //Background White, Opaque
case 0x21 : break; //Background White, Semi-transparent
case 0x22 : break; //
case 0x23 : break; //
case 0x24 : break; //
case 0x25 : break; //
case 0x26 : break; //
case 0x27 : break; //
case 0x28 : break; //
case 0x29 : break; //
case 0x2A : break; //
case 0x2B : break; //
case 0x2C : break; //
case 0x2D : break; //
case 0x2E : break; //
case 0x2F : break; //
default : Illegal(0x10, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special_11(int8u cc_data_2)
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
switch (cc_data_2)
{
//CEA-608-E, Section F.1.1.3
case 0x20 : //White
case 0x21 : //White Underline
case 0x22 : //
case 0x23 : //
case 0x24 : //
case 0x25 : //
case 0x26 : //
case 0x27 : //
case 0x28 : //
case 0x29 : //
case 0x2A : //
case 0x2B : //
case 0x2C : //
case 0x2D : //
case 0x2E : //
case 0x2F : //
//Color or Italic
if ((cc_data_2&0xFE)==0x2E) //Italic
Streams[StreamPos]->Attribute_Current|=Attribute_Italic;
else //Other attributes
Streams[StreamPos]->Attribute_Current=(cc_data_2&0x0F)>>1;
//Underline
if (cc_data_2&0x01)
Streams[StreamPos]->Attribute_Current|=Attribute_Underline;
break;
//CEA-608-E, Section F.1.1.1
case 0x30 : Character_Fill(L'\u2122'); break; //Registered mark symbol
case 0x31 : Character_Fill(L'\u00B0'); break; //Degree sign
case 0x32 : Character_Fill(L'\u00BD'); break; //1/2
case 0x33 : Character_Fill(L'\u00BF'); break; //interogation mark inverted
case 0x34 : Character_Fill(L'\u00A9'); break; //Trademark symbol
case 0x35 : Character_Fill(L'\u00A2'); break; //Cents sign
case 0x36 : Character_Fill(L'\u00A3'); break; //Pounds Sterling sign
case 0x37 : Character_Fill(L'\u266A'); break; //Music note
case 0x38 : Character_Fill(L'\u00E0'); break; //a grave
case 0x39 : Character_Fill(L' ' ); break; //Transparent space
case 0x3A : Character_Fill(L'\u00E8'); break; //e grave
case 0x3B : Character_Fill(L'\u00E2'); break; //a circumflex
case 0x3C : Character_Fill(L'\u00EA'); break; //e circumflex
case 0x3D : Character_Fill(L'\u00EE'); break; //i circumflex
case 0x3E : Character_Fill(L'\u00F4'); break; //o circumflex
case 0x3F : Character_Fill(L'\u00FB'); break; //u circumflex
default : Illegal(0x11, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special_12(int8u cc_data_2)
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
if (Streams[StreamPos]->x && cc_data_2>=0x20 && cc_data_2<0x40)
Streams[StreamPos]->x--; //Erasing previous character
switch (cc_data_2)
{
//CEA-608-E, Section 6.4.2
case 0x20 : Character_Fill(L'\u00C1'); break; //A with acute
case 0x21 : Character_Fill(L'\u00C9'); break; //E with acute
case 0x22 : Character_Fill(L'\u00D3'); break; //O with acute
case 0x23 : Character_Fill(L'\u00DA'); break; //U with acute
case 0x24 : Character_Fill(L'\u00DC'); break; //U withdiaeresis or umlaut
case 0x25 : Character_Fill(L'\u00FC'); break; //u with diaeresis or umlaut
case 0x26 : Character_Fill(L'\'' ); break; //opening single quote
case 0x27 : Character_Fill(L'\u00A1'); break; //inverted exclamation mark
case 0x28 : Character_Fill(L'*' ); break; //Asterisk
case 0x29 : Character_Fill(L'\'' ); break; //plain single quote
case 0x2A : Character_Fill(L'\u2014'); break; //em dash
case 0x2B : Character_Fill(L'\u00A9'); break; //Copyright
case 0x2C : Character_Fill(L'\u2120'); break; //Servicemark
case 0x2D : Character_Fill(L'\u2022'); break; //round bullet
case 0x2E : Character_Fill(L'\u2120'); break; //opening double quotes
case 0x2F : Character_Fill(L'\u2121'); break; //closing double quotes
case 0x30 : Character_Fill(L'\u00C0'); break; //A with grave accent
case 0x31 : Character_Fill(L'\u00C2'); break; //A with circumflex accent
case 0x32 : Character_Fill(L'\u00C7'); break; //C with cedilla
case 0x33 : Character_Fill(L'\u00C8'); break; //E with grave accent
case 0x34 : Character_Fill(L'\u00CA'); break; //E with circumflex accent
case 0x35 : Character_Fill(L'\u00CB'); break; //E with diaeresis or umlaut mark
case 0x36 : Character_Fill(L'\u00EB'); break; //e with diaeresis or umlaut mark
case 0x37 : Character_Fill(L'\u00CE'); break; //I with circumflex accent
case 0x38 : Character_Fill(L'\u00CF'); break; //I with diaeresis or umlaut mark
case 0x39 : Character_Fill(L'\u00EF'); break; //i with diaeresis or umlaut mark
case 0x3A : Character_Fill(L'\u00D4'); break; //O with circumflex
case 0x3B : Character_Fill(L'\u00D9'); break; //U with grave accent
case 0x3C : Character_Fill(L'\u00F9'); break; //u with grave accent
case 0x3D : Character_Fill(L'\u00D9'); break; //U with circumflex accent
case 0x3E : Character_Fill(L'\u00AB'); break; //opening guillemets
case 0x3F : Character_Fill(L'\u00BB'); break; //closing guillemets
default : Illegal(0x12, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special_13(int8u cc_data_2)
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
if (Streams[StreamPos]->x && cc_data_2>=0x20 && cc_data_2<0x40)
Streams[StreamPos]->x--; //Erasing previous character
switch (cc_data_2)
{
//CEA-608-E, Section 6.4.2
case 0x20 : Character_Fill(L'\u00C3'); break; //A with tilde
case 0x21 : Character_Fill(L'\u00E3'); break; //a with tilde
case 0x22 : Character_Fill(L'\u00CD'); break; //I with acute accent
case 0x23 : Character_Fill(L'\u00CC'); break; //I with grave accent
case 0x24 : Character_Fill(L'\u00EC'); break; //i with grave accent
case 0x25 : Character_Fill(L'\u00D2'); break; //O with grave accent
case 0x26 : Character_Fill(L'\u00E2'); break; //o with grave accent
case 0x27 : Character_Fill(L'\u00D5'); break; //O with tilde
case 0x28 : Character_Fill(L'\u00F5'); break; //o with tilde
case 0x29 : Character_Fill(L'{' ); break; //opening brace
case 0x2A : Character_Fill(L'}' ); break; //closing brace
case 0x2B : Character_Fill(L'\\' ); break; //backslash
case 0x2C : Character_Fill(L'^' ); break; //caret
case 0x2D : Character_Fill(L'_' ); break; //Underbar
case 0x2E : Character_Fill(L'|' ); break; //pipe
case 0x2F : Character_Fill(L'~' ); break; //tilde
case 0x30 : Character_Fill(L'\u00C4'); break; //A with diaeresis or umlaut mark
case 0x31 : Character_Fill(L'\u00E4'); break; //a with diaeresis or umlaut mark
case 0x32 : Character_Fill(L'\u00D6'); break; //o with diaeresis or umlaut mark
case 0x33 : Character_Fill(L'\u00F6'); break; //o with diaeresis or umlaut mark
case 0x34 : Character_Fill(L'\u00DF'); break; //eszett (mall sharp s)
case 0x35 : Character_Fill(L'\u00A5'); break; //yen
case 0x36 : Character_Fill(L'\u00A4'); break; //non-specific currency sign
case 0x37 : Character_Fill(L'\u23D0'); break; //Vertical bar
case 0x38 : Character_Fill(L'\u00C5'); break; //I with diaeresis or umlaut mark
case 0x39 : Character_Fill(L'\u00E5'); break; //i with diaeresis or umlaut mark
case 0x3A : Character_Fill(L'\u00D8'); break; //O with ring
case 0x3B : Character_Fill(L'\u00F8'); break; //a with ring
case 0x3C : Character_Fill(L'\u23A1'); break; //upper left corner
case 0x3D : Character_Fill(L'\u23A4'); break; //upper right corner
case 0x3E : Character_Fill(L'\u23A3'); break; //lower left corner
case 0x3F : Character_Fill(L'\u23A6'); break; //lower right corner
default : Illegal(0x13, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special_14(int8u cc_data_2)
{
size_t StreamPos=TextMode*2+DataChannelMode;
switch (cc_data_2)
{
case 0x20 : //RCL - Resume Caption Loading
case 0x25 : //RU2 - Roll-Up Captions�2 Rows
case 0x26 : //RU3 - Roll-Up Captions�3 Rows
case 0x27 : //RU4 - Roll-Up Captions�4 Rows
case 0x29 : //RDC - Resume Direct Captioning
case 0x2A : //TR - Text Restart
case 0x2B : //RTD - Resume Text Display
case 0x2C : //EDM - Erase Displayed Memory
TextMode=(cc_data_2&0xFE)==0x2A;
XDS_Level=(size_t)-1; // No more XDS
StreamPos=TextMode*2+DataChannelMode;
//Alloc
if (StreamPos>=Streams.size())
Streams.resize(StreamPos+1);
if (Streams[StreamPos]==NULL)
{
Streams[StreamPos]=new stream();
Streams[StreamPos]->CC_Displayed.resize(Eia608_Rows);
for (size_t Pos=0; Pos<Streams[StreamPos]->CC_Displayed.size(); Pos++)
Streams[StreamPos]->CC_Displayed[Pos].resize(Eia608_Columns);
if (StreamPos<2) //CC only, not in Text
{
Streams[StreamPos]->CC_NonDisplayed.resize(Eia608_Rows);
for (size_t Pos=0; Pos<Streams[StreamPos]->CC_NonDisplayed.size(); Pos++)
Streams[StreamPos]->CC_NonDisplayed[Pos].resize(Eia608_Columns);
}
}
Streams[StreamPos]->Synched=true;
break;
case 0x2F : //EOC - end of Caption
TextMode=false;
StreamPos=TextMode*2+DataChannelMode;
default: ;
}
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
switch (cc_data_2)
{
case 0x20 : TextMode=false;
Streams[StreamPos]->InBack=true;
break; //RCL - Resume Caption Loading (Select pop-on style)
case 0x21 : if (Streams[StreamPos]->x)
Streams[StreamPos]->x--;
(Streams[StreamPos]->InBack?Streams[StreamPos]->CC_NonDisplayed:Streams[StreamPos]->CC_Displayed)[Streams[StreamPos]->y][Streams[StreamPos]->x].Value=L' '; //Clear the character
if (!Streams[StreamPos]->InBack)
HasChanged();
break; //BS - Backspace
case 0x22 : Special_14(0x2D); //Found 1 file with AOF and non CR
break; //AOF - Alarm Off
case 0x23 : break; //AON - Alarm On
case 0x24 : for (size_t Pos=Streams[StreamPos]->x; Pos<Eia608_Columns; Pos++)
(Streams[StreamPos]->InBack?Streams[StreamPos]->CC_NonDisplayed:Streams[StreamPos]->CC_Displayed)[Streams[StreamPos]->y][Pos].Value=L' '; //Clear up to the end of line
if (!Streams[StreamPos]->InBack)
HasChanged();
break; //DER - Delete to End of Row
case 0x25 : //RU2 - Roll-Up Captions�2 Rows
case 0x26 : //RU3 - Roll-Up Captions�3 Rows
case 0x27 : //RU4 - Roll-Up Captions�4 Rows
Streams[StreamPos]->RollUpLines=cc_data_2-0x25+2;
Streams[StreamPos]->InBack=false;
break; //RUx - Roll-Up Captions�x Rows
case 0x28 : break; //FON - Flash On
case 0x29 : Streams[StreamPos]->InBack=false;
break; //RDC - Resume Direct Captioning (paint-on style)
case 0x2A : TextMode=true;
Streams[StreamPos]->RollUpLines=Eia608_Rows; //Roll up all the lines
Streams[StreamPos]->y=Eia608_Rows-1; //Base is the bottom line
Streams[StreamPos]->Attribute_Current=0; //Reset all attributes
Special_14(0x2D); //Next line
break; //TR - Text Restart (clear Text, but not boxes)
case 0x2B : TextMode=true;
break; //RTD - Resume Text Display
case 0x2C :
if (StreamPos<Streams.size() && Streams[StreamPos])
{
bool HasChanged_=false;
for (size_t Pos_Y=0; Pos_Y<Eia608_Rows; Pos_Y++)
for (size_t Pos_X=0; Pos_X<Eia608_Columns; Pos_X++)
if (Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Value!=L' ')
{
Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Value=L' ';
Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Attribute=0;
HasChanged_=true;
}
if (HasChanged_)
HasChanged();
}
break; //EDM - Erase Displayed Memory
case 0x2D : for (size_t Pos=1; Pos<Streams[StreamPos]->RollUpLines; Pos++)
{
if (Streams[StreamPos]->y>=Streams[StreamPos]->RollUpLines-Pos && Streams[StreamPos]->y-Streams[StreamPos]->RollUpLines+Pos+1<Eia608_Rows)
Streams[StreamPos]->CC_Displayed[Streams[StreamPos]->y-Streams[StreamPos]->RollUpLines+Pos]=Streams[StreamPos]->CC_Displayed[Streams[StreamPos]->y-Streams[StreamPos]->RollUpLines+Pos+1];
}
for (size_t Pos_X=0; Pos_X<Eia608_Columns; Pos_X++)
{
Streams[StreamPos]->CC_Displayed[Streams[StreamPos]->y][Pos_X].Value=L' ';
Streams[StreamPos]->CC_Displayed[Streams[StreamPos]->y][Pos_X].Attribute=0;
}
if (!Streams[StreamPos]->InBack)
HasChanged();
Streams[StreamPos]->x=0;
break; //CR - Carriage Return
case 0x2E : for (size_t Pos_Y=0; Pos_Y<Streams[StreamPos]->CC_NonDisplayed.size(); Pos_Y++)
for (size_t Pos_X=0; Pos_X<Streams[StreamPos]->CC_NonDisplayed[Pos_Y].size(); Pos_X++)
{
Streams[StreamPos]->CC_NonDisplayed[Pos_Y][Pos_X].Value=L' ';
Streams[StreamPos]->CC_NonDisplayed[Pos_Y][Pos_X].Attribute=0;
}
break; //ENM - Erase Non-Displayed Memory
case 0x2F : Streams[StreamPos]->CC_Displayed.swap(Streams[StreamPos]->CC_NonDisplayed);
HasChanged();
Streams[StreamPos]->Synched=false;
break; //EOC - End of Caption
default : Illegal(0x14, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Special_17(int8u cc_data_2)
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
switch (cc_data_2)
{
//CEA-608-E, section B.4
case 0x21 : //TO1 - Tab Offset 1 Column
case 0x22 : //TO2 - Tab Offset 2 Columns
case 0x23 : //TO3 - Tab Offset 3 Columns
Streams[StreamPos]->x+=cc_data_2&0x03;
if (Streams[StreamPos]->x>=Eia608_Columns)
Streams[StreamPos]->x=Eia608_Columns-1;
break;
//CEA-608-E, section 6.3
case 0x24 : break; //Select the standard line 21 character set in normal size
case 0x25 : break; //Select the standard line 21 character set in double size
case 0x26 : break; //Select the first private character set
case 0x27 : break; //Select the second private character set
case 0x28 : break; //Select the People's Republic of China character set: GB 2312-80
case 0x29 : break; //Select the Korean Standard character set: KSC 5601-1987
case 0x2A : break; //Select the first registered character set
//CEA-608-E, section 6.2
case 0x2D : break; //Background Transparent
case 0x2E : break; //Foreground Black
case 0x2F : break; //Foreground Black Underline
default : Illegal(0x17, cc_data_2);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Standard(int8u Character)
{
switch (Character)
{
//CEA-608-E, Section F.1.1.2
case 0x20 : Character_Fill(L' ' ); break;
case 0x21 : Character_Fill(L'!' ); break;
case 0x22 : Character_Fill(L'"' ); break;
case 0x23 : Character_Fill(L'#' ); break;
case 0x24 : Character_Fill(L'$' ); break;
case 0x25 : Character_Fill(L'%' ); break;
case 0x26 : Character_Fill(L'&' ); break;
case 0x27 : Character_Fill(L'\'' ); break;
case 0x28 : Character_Fill(L'(' ); break;
case 0x29 : Character_Fill(L')' ); break;
case 0x2A : Character_Fill(L'\xE1' ); break; //a acute
case 0x2B : Character_Fill(L'+' ); break;
case 0x2C : Character_Fill(L',' ); break;
case 0x2D : Character_Fill(L'-' ); break;
case 0x2E : Character_Fill(L'.' ); break;
case 0x2F : Character_Fill(L'/' ); break;
case 0x30 : Character_Fill(L'0' ); break;
case 0x31 : Character_Fill(L'1' ); break;
case 0x32 : Character_Fill(L'2' ); break;
case 0x33 : Character_Fill(L'3' ); break;
case 0x34 : Character_Fill(L'4' ); break;
case 0x35 : Character_Fill(L'5' ); break;
case 0x36 : Character_Fill(L'6' ); break;
case 0x37 : Character_Fill(L'7' ); break;
case 0x38 : Character_Fill(L'8' ); break;
case 0x39 : Character_Fill(L'9' ); break;
case 0x3A : Character_Fill(L':' ); break;
case 0x3B : Character_Fill(L';' ); break;
case 0x3C : Character_Fill(L'<' ); break;
case 0x3E : Character_Fill(L'>' ); break;
case 0x3F : Character_Fill(L'?' ); break;
case 0x40 : Character_Fill(L'@' ); break;
case 0x41 : Character_Fill(L'A' ); break;
case 0x42 : Character_Fill(L'B' ); break;
case 0x43 : Character_Fill(L'C' ); break;
case 0x44 : Character_Fill(L'D' ); break;
case 0x45 : Character_Fill(L'E' ); break;
case 0x46 : Character_Fill(L'F' ); break;
case 0x47 : Character_Fill(L'G' ); break;
case 0x48 : Character_Fill(L'H' ); break;
case 0x49 : Character_Fill(L'I' ); break;
case 0x4A : Character_Fill(L'J' ); break;
case 0x4B : Character_Fill(L'K' ); break;
case 0x4C : Character_Fill(L'L' ); break;
case 0x4D : Character_Fill(L'M' ); break;
case 0x4E : Character_Fill(L'N' ); break;
case 0x4F : Character_Fill(L'O' ); break;
case 0x50 : Character_Fill(L'P' ); break;
case 0x51 : Character_Fill(L'Q' ); break;
case 0x52 : Character_Fill(L'R' ); break;
case 0x53 : Character_Fill(L'S' ); break;
case 0x54 : Character_Fill(L'T' ); break;
case 0x55 : Character_Fill(L'U' ); break;
case 0x56 : Character_Fill(L'V' ); break;
case 0x57 : Character_Fill(L'W' ); break;
case 0x58 : Character_Fill(L'X' ); break;
case 0x59 : Character_Fill(L'Y' ); break;
case 0x5A : Character_Fill(L'Z' ); break;
case 0x5B : Character_Fill(L'[' ); break;
case 0x5C : Character_Fill(L'\xE9' ); break; //e acute
case 0x5D : Character_Fill(L']' ); break;
case 0x5E : Character_Fill(L'\xED' ); break; //i acute
case 0x5F : Character_Fill(L'\xF3' ); break; //o acute
case 0x60 : Character_Fill(L'\xFA' ); break; //u acute
case 0x61 : Character_Fill(L'a' ); break;
case 0x62 : Character_Fill(L'b' ); break;
case 0x63 : Character_Fill(L'c' ); break;
case 0x64 : Character_Fill(L'd' ); break;
case 0x65 : Character_Fill(L'e' ); break;
case 0x66 : Character_Fill(L'f' ); break;
case 0x67 : Character_Fill(L'g' ); break;
case 0x68 : Character_Fill(L'h' ); break;
case 0x69 : Character_Fill(L'i' ); break;
case 0x6A : Character_Fill(L'j' ); break;
case 0x6B : Character_Fill(L'k' ); break;
case 0x6C : Character_Fill(L'l' ); break;
case 0x6D : Character_Fill(L'm' ); break;
case 0x6E : Character_Fill(L'n' ); break;
case 0x6F : Character_Fill(L'o' ); break;
case 0x70 : Character_Fill(L'p' ); break;
case 0x71 : Character_Fill(L'q' ); break;
case 0x72 : Character_Fill(L'r' ); break;
case 0x73 : Character_Fill(L's' ); break;
case 0x74 : Character_Fill(L't' ); break;
case 0x75 : Character_Fill(L'u' ); break;
case 0x76 : Character_Fill(L'v' ); break;
case 0x77 : Character_Fill(L'w' ); break;
case 0x78 : Character_Fill(L'x' ); break;
case 0x79 : Character_Fill(L'y' ); break;
case 0x7A : Character_Fill(L'z' ); break;
case 0x7B : Character_Fill(L'\xE7' ); break; //c with cedilla
case 0x7C : Character_Fill(L'\xF7' ); break; //division symbol
case 0x7D : Character_Fill(L'\xD1' ); break; //N tilde
case 0x7E : Character_Fill(L'\xF1' ); break; //n tilde
case 0x7F : Character_Fill(L'\x25A0'); break; //Solid block
default : Illegal(0x00, Character);
}
}
//---------------------------------------------------------------------------
void File_Eia608::Character_Fill(wchar_t Character)
{
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos>=Streams.size() || Streams[StreamPos]==NULL || !Streams[StreamPos]->Synched)
return; //Not synched
if (Streams[StreamPos]->x==Eia608_Columns)
{
Streams[StreamPos]->x--; //There is a problem
//TODO: Put it at the end, for the conversion
//TODO: Handle special chars
}
if (Streams[StreamPos]->InBack)
Streams[StreamPos]->CC_NonDisplayed[Streams[StreamPos]->y][Streams[StreamPos]->x].Value=Character;
else
Streams[StreamPos]->CC_Displayed[Streams[StreamPos]->y][Streams[StreamPos]->x].Value=Character;
Streams[StreamPos]->x++;
if (TextMode || !Streams[StreamPos]->InBack)
HasChanged();
if (!HasContent)
HasContent=true;
DataDetected[1+StreamPos]=true;
}
//---------------------------------------------------------------------------
void File_Eia608::HasChanged()
{
#if MEDIAINFO_EVENTS
size_t StreamPos=TextMode*2+DataChannelMode;
if (StreamPos<Streams.size() && Streams[StreamPos])
EVENT_BEGIN (Eia608, CC_Content, 0)
Event.Field=1+cc_type;
Event.MuxingMode=MuxingMode;
Event.Service=1+(TextMode?1:0)*2+(DataChannelMode?1:0);
Event.StreamIDs[Event.StreamIDs_Size-1]=Event.Service;
for (size_t Pos_Y=0; Pos_Y<Streams[StreamPos]->CC_Displayed.size(); Pos_Y++)
{
for (size_t Pos_X=0; Pos_X<Streams[StreamPos]->CC_Displayed[Pos_Y].size(); Pos_X++)
{
Event.Row_Values[Pos_Y][Pos_X]=Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Value;
Event.Row_Attributes[Pos_Y][Pos_X]=Streams[StreamPos]->CC_Displayed[Pos_Y][Pos_X].Attribute;
}
Event.Row_Values[Pos_Y][32]=L'\0';
}
EVENT_END ()
#endif //MEDIAINFO_EVENTS
}
//---------------------------------------------------------------------------
void File_Eia608::Illegal(int8u cc_data_1, int8u cc_data_2)
{
}
//***************************************************************************
// C++
//***************************************************************************
} //NameSpace
#endif //MEDIAINFO_EIA608_YES
↑ V547 Expression 'Streams[StreamPos]' is always true.
↑ V560 A part of conditional expression is always true: Streams[StreamPos].
↑ V1037 Two or more case-branches perform the same actions. Check lines: 436, 443
↑ V1037 Two or more case-branches perform the same actions. Check lines: 750, 753
↑ V1037 Two or more case-branches perform the same actions. Check lines: 756, 758
↑ V1037 Two or more case-branches perform the same actions. Check lines: 771, 773
↑ V525 The code contains the collection of similar blocks. Check items '2', '3', '3' in lines 446, 448, 450.