+ h->b_xds = false;
+ h->i_class = XDS_MAX_CLASS_COUNT;
+ h->i_type = 0;
+ h->b_future = false;
+ for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
+ {
+ for( j = 0; j < 128; j++ )
+ h->pkt[i][j].b_started = false;
+ }
+ h->b_meta_changed = false;
+ memset( &h->meta, 0, sizeof(h->meta) );
+}
+static void XdsExit( xds_t *h )
+{
+ /* */
+ free( h->meta.psz_channel_name );
+ free( h->meta.psz_channel_call_letter );
+ free( h->meta.psz_channel_number );
+
+ /* */
+ free( h->meta.current.psz_name );
+ free( h->meta.current.psz_rating );
+ /* */
+ free( h->meta.future.psz_name );
+ free( h->meta.future.psz_rating );
+}
+static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
+{
+ int i;
+ int i_dst;
+
+ for( i = 0, i_dst = 0; i < i_src; i++ )
+ {
+ switch( p_src[i] )
+ {
+#define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
+ E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
+ E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
+ E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
+ E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
+ E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
+ E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
+ E2( 0x7c, 0xc3,0xb7); // division symbol
+ E2( 0x7d, 0xc3,0x91); // uppercase N tilde
+ E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
+#undef E2
+ default:
+ dst[i_dst++] = p_src[i];
+ break;
+ }
+ }
+ dst[i_dst++] = '\0';
+}
+static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
+{
+ if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
+ return false;
+ if( *ppsz_dst == NULL && psz_new == NULL )
+ return false;
+
+ free( *ppsz_dst );
+ if( psz_new )
+ *ppsz_dst = strdup( psz_new );
+ else
+ *ppsz_dst = NULL;
+
+ h->b_meta_changed = true;
+ return true;
+}
+
+static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
+{
+ xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
+ char name[2*32+1];
+ int i_rating;
+
+ switch( h->i_type )
+ {
+ case 0x03:
+ XdsStringUtf8( name, pk->p_data, pk->i_data );
+ if( XdsChangeString( h, &p_prg->psz_name, name ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
+ }
+ break;
+ case 0x05:
+ i_rating = (pk->p_data[0] & 0x18);
+ if( i_rating == 0x08 )
+ {
+ /* TPG */
+ static const char *pppsz_ratings[8][2] = {
+ { "None", "No rating (no content advisory)" },
+ { "TV-Y", "All Children (no content advisory)" },
+ { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" },
+ { "TV-G", "General Audience (no content advisory)" },
+ { "TV-PG", "Parental Guidance Suggested" },
+ { "TV-14", "Parents Strongly Cautioned" },
+ { "TV-MA", "Mature Audience Only" },
+ { "None", "No rating (no content advisory)" }
+ };
+ p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
+ if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
+ // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
+ }
+ }
+ else if( i_rating == 0x00 || i_rating == 0x10 )
+ {
+ /* MPAA */
+ static const char *pppsz_ratings[8][2] = {
+ { "N/A", "N/A" },
+ { "G", "General Audiences" },
+ { "PG", "Parental Guidance Suggested" },
+ { "PG-13", "Parents Strongly Cautioned" },
+ { "R", "Restricted" },
+ { "NC-17", "No one 17 and under admitted" },
+ { "X", "No one under 17 admitted" },
+ { "NR", "Not Rated" },
+ };
+ p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
+ if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
+ // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
+ }
+ }
+ else
+ {
+ /* Non US Rating TODO */
+ assert( i_rating == 0x18 ); // only left value possible */
+ p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
+ if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
+ }
+ }
+ break;
+
+ default:
+#ifdef TY_XDS_DEBUG
+ fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
+#endif
+ break;
+ }
+}
+
+static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
+{
+ char name[2*32+1];
+ char chan[2*32+1];
+
+ switch( h->i_type )
+ {
+ case 0x01:
+ if( pk->i_data < 2 )
+ return;
+ XdsStringUtf8( name, pk->p_data, pk->i_data );
+ if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
+ }
+ break;
+
+ case 0x02:
+ if( pk->i_data < 4 )
+ return;
+
+ XdsStringUtf8( name, pk->p_data, 4 );
+ if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
+ }
+ if( pk->i_data >= 6 )
+ {
+ XdsStringUtf8( chan, &pk->p_data[4], 2 );
+ if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
+ }
+ }
+ else
+ {
+ if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
+ }
+ }
+ break;
+ case 0x03:
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
+ break;
+ case 0x04:
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
+ break;
+ default:
+#ifdef TY_XDS_DEBUG
+ fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
+#endif
+ break;
+ }
+}
+
+static void XdsDecode( xds_t *h, xds_packet_t *pk )
+{
+ switch( h->i_class )
+ {
+ case XDS_CLASS_CURRENT:
+ case XDS_CLASS_FUTURE:
+ XdsDecodeCurrentFuture( h, pk );
+ break;
+ case XDS_CLASS_CHANNEL:
+ XdsDecodeChannel( h, pk );
+ break;
+ case XDS_CLASS_MISCELLANEOUS:
+#ifdef TY_XDS_DEBUG
+ fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
+#endif
+ break;
+ case XDS_CLASS_PUBLIC_SERVICE:
+#ifdef TY_XDS_DEBUG
+ fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
+#endif
+ break;
+ default:
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
+ break;
+ }
+}
+
+static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
+{
+ /* TODO check parity */
+ d1 &= 0x7f;
+ d2 &= 0x7f;
+
+ /* */
+ if( d1 >= 0x01 && d1 <= 0x0e )
+ {
+ const xds_class_t i_class = ( d1 - 1 ) >> 1;
+ const int i_type = d2;
+ const bool b_start = d1 & 0x01;
+ xds_packet_t *pk = &h->pkt[i_class][i_type];
+
+ if( !b_start && !pk->b_started )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
+ h->b_xds = false;
+ return;
+ }
+
+ h->b_xds = true;
+ h->i_class = i_class;
+ h->i_type = i_type;
+ h->b_future = !b_start;
+ pk->b_started = true;
+ if( b_start )
+ {
+ pk->i_data = 0;
+ pk->i_sum = d1 + d2;
+ }
+ }
+ else if( d1 == 0x0f && h->b_xds )
+ {
+ xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
+
+ /* TODO checksum and decode */
+ pk->i_sum += d1 + d2;
+ if( pk->i_sum & 0x7f )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
+ pk->b_started = false;
+ return;
+ }
+ if( pk->i_data <= 0 )
+ {
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
+ pk->b_started = false;
+ return;
+ }
+
+ //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
+ // pk->i_data--;
+ XdsDecode( h, pk );
+
+ /* Reset it */
+ pk->b_started = false;
+ }
+ else if( d1 >= 0x20 && h->b_xds )
+ {
+ xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
+
+ if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
+ {
+ /* Broken -> reinit */
+ //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
+ h->b_xds = false;
+ pk->b_started = false;
+ return;
+ }
+ /* TODO check parity bit */
+ pk->p_data[pk->i_data++] = d1 & 0x7f;
+ pk->p_data[pk->i_data++] = d2 & 0x7f;
+ pk->i_sum += d1+d2;
+ }
+ else
+ {
+ h->b_xds = false;
+ }
+}
+
+static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )