The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * parse_si.c
 *
 *  Created on: 2 Apr 2011
 *      Author: sdprice1
 */


// VERSION = 1.01

/*=============================================================================================*/
// USES
/*=============================================================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>

#include "parse_si.h"

#include "tables/parse_si_pat.h"  /* 0x00 */
#include "tables/parse_si_cat.h"  /* 0x01 */
#include "tables/parse_si_pmt.h"  /* 0x02 */
#include "tables/parse_si_nit.h"  /* 0x40 */
#include "tables/parse_si_sdt.h"  /* 0x42 */
#include "tables/parse_si_bat.h"  /* 0x4a */
#include "tables/parse_si_eit.h"  /* 0x4e */
#include "tables/parse_si_tdt.h"  /* 0x70 */
#include "tables/parse_si_rst.h"  /* 0x71 */
#include "tables/parse_si_st.h"  /* 0x72 */
#include "tables/parse_si_tot.h"  /* 0x73 */
#include "tables/parse_si_cit.h"  /* 0x77 */
#include "tables/parse_si_dit.h"  /* 0x7e */
#include "tables/parse_si_sit.h"  /* 0x7f */

/*=============================================================================================*/
// CONSTANTS
/*=============================================================================================*/

// Maximum section lengths
unsigned SECTION_MAX_LENGTHS[SECTION_MAX+1] = {
	// default max length is 1024 bytes (-1 header -2 crc)
	[0 ... SECTION_MAX]							= 1021,

	// These default to 4043
	[SECTION_ST]							 	= 4093,
	[SECTION_EIT_START ... SECTION_EIT_END] 	= 4093,
	[SECTION_SIT]							 	= 4093,
	[SECTION_CIT]							 	= 4093,

	// This is a funny!
	[SECTION_DIT]							 	= 1
} ;

// Section syntax values
#define SYNTAX_0			0x00
#define SYNTAX_1			0x80
#define SYNTAX_EITHER		0xFF
unsigned SECTION_SYNTAX[SECTION_MAX+1] = {
	// Most have the bit set
	[0 ... SECTION_MAX]							= SYNTAX_1,

	// These have bit 0
	[SECTION_TDT]							 	= SYNTAX_0,
	[SECTION_TOT]							 	= SYNTAX_0,
	[SECTION_RST]							 	= SYNTAX_0,
	[SECTION_DIT]							 	= SYNTAX_0,

	// Special
	[SECTION_ST]							 	= SYNTAX_EITHER,

} ;


/*=============================================================================================*/
// MACROS
/*=============================================================================================*/


/*=============================================================================================*/
// FUNCTIONS
/*=============================================================================================*/

static void dummy_handler(struct TS_reader *tsreader, struct TS_state *tsstate, struct Section *section, void *user_data)
{

}


/* ----------------------------------------------------------------------- */
void print_si(struct Section *section)
{
	switch(section->table_id)
	{

		// PAT
		case SECTION_PAT:
			print_pat((struct Section_program_association *)section) ;
			break;

		// CAT
		case SECTION_CAT:
			print_cat((struct Section_conditional_access *)section) ;
			break;

		// PMT
		case SECTION_PMT:
			print_pmt((struct Section_program_map *)section) ;
			break;

		// NIT this
		case SECTION_NIT_ACTUAL:
		// NIT other
		case SECTION_NIT_OTHER:
			print_nit((struct Section_network_information *)section) ;
			break;

		// SDT this
		case SECTION_SDT_ACTUAL:
		// SDT other
		case SECTION_SDT_OTHER:
			print_sdt((struct Section_service_description *)section) ;
			break;

		// BAT
		case SECTION_BAT:
			print_bat((struct Section_bouquet_association *)section) ;
			break;

		// Now/Next this
		case SECTION_EIT_NOW_ACTUAL:
		// Now/Next other
		case SECTION_EIT_NOW_OTHER:
		// EIT this
		case SECTION_EIT_ACTUAL_START ... SECTION_EIT_ACTUAL_END:
		// EIT other
		case SECTION_EIT_OTHER_START ... SECTION_EIT_OTHER_END:
			print_eit((struct Section_event_information *)section) ;
			break;

		// TDT
		case SECTION_TDT:
			print_tdt((struct Section_time_date *)section) ;
			break;

		// RST
		case SECTION_RST:
			print_rst((struct Section_running_status *)section) ;
			break;

		// ST
		case SECTION_ST:
			print_st((struct Section_stuffing *)section) ;
			break;

		// TOT
		case SECTION_TOT:
			print_tot((struct Section_time_offset *)section) ;
			break;

		// CIT
		case SECTION_CIT:
			print_cit((struct Section_content_identifier *)section) ;
			break;

		// DIT
		case SECTION_DIT:
			print_dit((struct Section_discontinuity_information *)section) ;
			break;

		// SIT
		case SECTION_SIT:
			print_sit((struct Section_selection_information *)section) ;
			break;


		default:
			break;
	}

}

/* ----------------------------------------------------------------------- */
int parse_si(struct TS_reader *tsreader, struct TS_state *tsstate, uint8_t *payload, unsigned payload_len)
{
unsigned table_id ;
unsigned section_len ;
unsigned section_syntax ;
unsigned max_section_len ;
unsigned expected_syntax ;
int ptr ;
int payload_left = payload_len ;
Section_handler handler = NULL ;
struct Section_decode_flags	flags ;

	tsparse_dbg_prt(10, ("\n== parse_si() : PID 0x%02x : payload len %d [0x%02x] ==\n", tsstate->pid_item->pidinfo.pid, payload_left, payload[0]));

	CHECK_TS_READER(tsreader) ;
	CHECK_TS_STATE(tsstate) ;
	CHECK_TS_PID(tsstate->pid_item) ;

//fprintf(stderr, "\n== parse_si() : payload len %d [0x%02x] ==\n", payload_left, payload[0]);
//fprintf(stderr, " + payload len %d\n", payload_left);
//fprintf(stderr, " + payload [0x%02x]\n", payload[0]);


	// keep processing while we've got some buffer left AND we're not at the stuffing bytes
	while ( (payload_left > (SI_HEADER_LEN+SI_CRC_LEN)) && (payload[0] != 0xff) )
	{
		tsparse_dbg_prt(10, ("\nparse_si() loop start: payload now:\n"));

//fprintf(stderr, " + loop start: payload left now = %d [payload buff @ %p]\n", payload_left, payload);

		if (tsreader->debug >= 104)
			dump_buff(&payload[0], payload_left, payload_left) ;


		//	pointer_field 8 uimsbf
		ptr = payload[0] ;

		// check to see if we've skipped off the end of the buffer!
		if ( (payload_left-ptr) < (SI_HEADER_LEN+SI_CRC_LEN) )
		{
//fprintf(stderr, " ** ptr=%d - Invalid PTR?\n", ptr);

			if (tsreader->error_hook)
			{
				SET_DVB_ERROR(ERR_SECTIONLEN) ;
				if (tsreader->error_hook)
					tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
			}

			return 0 ;
		}
//fprintf(stderr, " + + ptr=%d payload left now = %d [payload buff @ %p]\n", ptr, payload_left-ptr, &payload[ptr]);

		//	table_id 8 uimsbf
		//	section_syntax_indicator 1 bslbf
		//	indicator 1 bslbf
		//	reserved 2 bslbf
		//	section_length 12 uimsbf
		table_id = payload[ptr+1] & SECTION_MAX ;
		section_syntax = payload[ptr+2] & 0x80 ;

		max_section_len = SECTION_MAX_LENGTHS[table_id] ;
		expected_syntax = SECTION_SYNTAX[table_id] ;

		// number of bytes AFTER this field INCLUDING 4 byte CRC
		section_len = ((payload[ptr+2] & 0x0f)<<8) | payload[ptr+3] ;

		tsparse_dbg_prt(102, ("PSI pid %d Table %d Len %d : 0x%02x 0x%02x 0x%02x 0x%02x \n",
			tsstate->pidinfo.pid, table_id, section_len, payload[ptr+0], payload[ptr+1], payload[ptr+2], payload[ptr+3])) ;

		tsparse_dbg_prt(2, ("PSI pid 0x%x Table 0x%x [ptr 0x%02x] Sect Len %d : Payload left %d (syntax 0x%02x)\n",
				tsstate->pidinfo.pid, table_id,
				ptr,
				section_len, payload_left,
				section_syntax)) ;


		// error check
		if (section_len > max_section_len)
		{
			tsparse_dbg_prt(2, ("PSI pid 0x%x Table 0x%x : section length error : %d (max %d)\n",
					tsstate->pidinfo.pid, table_id,
					section_len,
					max_section_len)) ;

			tsstate->pid_item->pesinfo.psi_error++ ;
			tsstate->pidinfo.pid_error++ ;
			if (tsreader->error_hook)
			{
				SET_DVB_ERROR(ERR_SECTIONLEN) ;
				if (tsreader->error_hook)
					tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;
			}

			return 0 ;
		}
		else
		{
			// skip PTR & header
			payload_left -= SI_HEADER_LEN ;

			// get handler for this SI table - skip if none specified
			handler = tsreader->section_decode_table[table_id].handler ;
			flags = tsreader->section_decode_table[table_id].flags ;

			// check section fits into remaining buffer
			if ((section_len <= payload_left) && handler)
			{
				// check syntax
				if ((expected_syntax != SYNTAX_EITHER) && (section_syntax != expected_syntax))
				{
					tsparse_dbg_prt(2, ("Invalid section syntax 0x%02x (expected 0x%02x)\n", section_syntax, expected_syntax)) ;

					SET_DVB_ERROR(ERR_TSCORRUPT) ;
					if (tsreader->error_hook)
						tsreader->error_hook(dvb_error_code, &tsstate->pidinfo, tsreader->user_data) ;

					//TODO: Return here!
					//return 0 ;
				}

				// CRC covers whole packet from table_id to crc, need to extend length by section head
				uint32_t crc = crc32 (&payload[ptr+1], section_len+SECTION_HEADER_LEN);
				if (!crc)
				{
					tsparse_dbg_prt(100, ("**SI CRC PASS**\n")) ;
				}
				else
				{
					tsparse_dbg_prt(2, ("!!SI CRC FAIL!! - SI skipped\n")) ;
					return 0 ;
				}

				// ignore CRC in lower level decoding
				struct TS_bits *bits = bits_new(&payload[ptr+1], section_len+SECTION_HEADER_LEN-SI_CRC_LEN) ;


				switch(table_id)
				{

					// PAT
					case SECTION_PAT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_pat(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// CAT
					case SECTION_CAT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_cat(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// PMT
					case SECTION_PMT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_pmt(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// NIT this
					case SECTION_NIT_ACTUAL:
					// NIT other
					case SECTION_NIT_OTHER:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_nit(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// SDT this
					case SECTION_SDT_ACTUAL:
					// SDT other
					case SECTION_SDT_OTHER:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_sdt(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// BAT
					case SECTION_BAT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_bat(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// Now/Next this
					case SECTION_EIT_NOW_ACTUAL:
					// Now/Next other
					case SECTION_EIT_NOW_OTHER:
					// EIT this
					case SECTION_EIT_ACTUAL_START ... SECTION_EIT_ACTUAL_END:
					// EIT other
					case SECTION_EIT_OTHER_START ... SECTION_EIT_OTHER_END:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_eit(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// TDT
					case SECTION_TDT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_tdt(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// RST
					case SECTION_RST:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_rst(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// ST
					case SECTION_ST:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_st(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// TOT
					case SECTION_TOT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_tot(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// CIT
					case SECTION_CIT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_cit(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// DIT
					case SECTION_DIT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_dit(tsreader, tsstate, bits, handler, &flags) ;
						break;

					// SIT
					case SECTION_SIT:
						if (tsreader->debug >= 103)
							dump_buff(&payload[ptr+1], payload_left, section_len) ;
						parse_sit(tsreader, tsstate, bits, handler, &flags) ;
						break;


					default:
fprintf(stderr, "!! Unexpected Table 0x%02x !!\n", table_id) ;
						break;
				}

				bits_free(&bits) ;
			}
		}

		// skip to end of this section (ready for any subsequent section)
		unsigned section_total = (ptr+1) + SECTION_HEADER_LEN + section_len ;
		payload_left -= section_total ;
		payload += section_total ;

		tsparse_dbg_prt(10, (" + parse_si() end of loop : payload left %d\n", payload_left)) ;

	} // while

	return 0 ;
}