2019-07-13 22:11:28 +02:00
using System ;
using System.Globalization ;
2021-03-27 16:54:53 +01:00
using System.Text ;
2019-07-13 22:11:28 +02:00
using ChanSort.Api ;
2021-01-23 14:22:18 +01:00
namespace ChanSort.Loader.SatcoDX
2019-07-13 22:11:28 +02:00
{
2021-01-23 14:22:18 +01:00
internal class Channel : ChannelInfo
2019-07-13 22:11:28 +02:00
{
2025-01-28 21:26:53 +01:00
private readonly byte [ ] data ;
2019-07-13 22:11:28 +02:00
public int FileOffset { get ; }
public int Length { get ; }
2025-03-08 18:19:10 +01:00
private bool forceUtf8 ;
2019-07-13 22:11:28 +02:00
#region ctor ( )
2021-01-23 14:22:18 +01:00
internal Channel ( int pos , string line , byte [ ] data , int start , int length , DvbStringDecoder decoder )
2019-07-13 22:11:28 +02:00
{
2021-03-27 16:54:53 +01:00
this . data = data ;
2019-07-13 22:11:28 +02:00
this . FileOffset = start ;
this . Length = length ;
this . RecordIndex = pos ;
this . RecordOrder = this . OldProgramNr = pos + 1 ;
if ( ! line . StartsWith ( "SATCODX" ) )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( "Only SAT channels are supported" ) ;
2019-07-13 22:11:28 +02:00
if ( line . Length < 106 )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( "Unrecognized channel format" ) ;
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 10-27: satellite name
2025-03-08 18:19:10 +01:00
this . Satellite = line . Substring ( 10 , 18 ) . TrimEnd ( '\0' , ' ' , '_' ) ;
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 28: channel type
2019-07-13 22:11:28 +02:00
var type = line [ 28 ] ;
2023-06-03 10:38:11 +02:00
this . SignalSource = SignalSource . Dvb | SignalSource . Sat | ( type = = 'T' ? SignalSource . Tv : type = = 'R' ? SignalSource . Radio : 0 ) ;
2021-01-23 14:22:18 +01:00
this . ServiceTypeName = type = = 'T' ? "TV" : type = = 'R' ? "Radio" : type = = 'D' ? "Data" : "Other" ;
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 29-32: broadcast system
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 33-41: frequency in kHz
if ( int . TryParse ( line . Substring ( 33 , 9 ) , out var khz ) )
this . FreqInMhz = ( decimal ) khz / 1000 ;
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 42: polarity
this . Polarity = line [ 42 ] = = '1' ? 'H' : 'V' ;
2021-03-27 16:54:53 +01:00
// 43-50 + 115-126 in version 103 or 115-131 in version 105: channel name
this . ParseName ( decoder ) ;
2019-07-13 22:11:28 +02:00
2021-01-23 14:22:18 +01:00
// 51-54: sat position
2019-07-13 22:11:28 +02:00
var spos = line . Substring ( 51 , 4 ) . TrimStart ( '0' ) ;
this . SatPosition = spos . Substring ( 0 , spos . Length - 1 ) + CultureInfo . CurrentCulture . NumberFormat . NumberDecimalSeparator + spos . Substring ( spos . Length - 1 ) ;
2021-01-23 14:22:18 +01:00
// 69-73: symbol rate
2019-07-13 22:11:28 +02:00
if ( int . TryParse ( line . Substring ( 69 , 5 ) , out var symrate ) )
this . SymbolRate = symrate ;
2021-01-23 14:22:18 +01:00
// 74: FEC 0=-, 1=1/2, 2=2/3, 3=3/4, 5=5/6, 7=7/8
// 75-78: vpid or ____
// 79-82: apid or ____
// 83-86: pcrpid or ____
// 87-91: sid
2019-07-13 22:11:28 +02:00
if ( int . TryParse ( line . Substring ( 87 , 5 ) , out var sid ) )
this . ServiceId = sid ;
2021-01-23 14:22:18 +01:00
// 92-96: nid / onid
2019-07-13 22:11:28 +02:00
if ( int . TryParse ( line . Substring ( 92 , 5 ) , out var onid ) )
this . OriginalNetworkId = onid ;
2021-01-23 14:22:18 +01:00
// 97-101: tsid
2019-07-13 22:11:28 +02:00
if ( int . TryParse ( line . Substring ( 97 , 5 ) , out var tsid ) )
this . TransportStreamId = tsid ;
2021-01-23 14:22:18 +01:00
// 102-104: language
// 106-107: country code
// 108-110: language code
2025-01-28 21:26:53 +01:00
2021-01-23 14:22:18 +01:00
// 111-114: crypto code
2025-01-28 21:26:53 +01:00
if ( line . Length > = 115 )
this . Encrypted = line . Substring ( 111 , 4 ) = = "LCKD" ;
2019-07-13 22:11:28 +02:00
}
#endregion
2021-03-27 16:54:53 +01:00
#region ParseName ( )
/// <summary>
/// SATCODX103 files can contain channel names with unspecified implicit encoding, so we support reparsing based on a user selected default code page
/// </summary>
/// <param name="decoder"></param>
public void ParseName ( DvbStringDecoder decoder )
{
var length = this . Length ;
var start = this . FileOffset ;
// 43-50 + 115-126 in version 103 or 115-131 in version 105: channel name
byte [ ] nameBytes = new byte [ 8 + 17 ] ;
2025-03-08 18:19:10 +01:00
int lineEnd = Array . IndexOf ( data , ( byte ) ' \ x0A ' , start + 115 ) ;
var maxLen = lineEnd < 0 ? data . Length - start : lineEnd - start > = 132 ? 17 : 12 ;
var nameLen2 = Math . Min ( length - 115 , maxLen ) ; // version 103 has 12 extra bytes for channel name, version 105 has 17 (uploaded_service_list.sdx) or 12 (Panasonic Fire-OS Channels.sdx)
2021-03-27 16:54:53 +01:00
Array . Copy ( data , start + 43 , nameBytes , 0 , 8 ) ;
Array . Copy ( data , start + 115 , nameBytes , 8 , nameLen2 ) ;
// I have seen format 103 files using only implicit CP1252 encoding for Umlauts, as well as format 105 with implicit UTF-8/explicit DVB-encoding
var oldDefaultEncoding = decoder . DefaultEncoding ;
if ( nameLen2 > 12 )
2025-03-08 18:19:10 +01:00
{
2021-03-27 16:54:53 +01:00
decoder . DefaultEncoding = Encoding . UTF8 ;
2025-03-08 18:19:10 +01:00
this . forceUtf8 = true ;
}
2021-03-27 16:54:53 +01:00
decoder . GetChannelNames ( nameBytes , 0 , nameBytes . Length , out var longName , out var shortName ) ;
decoder . DefaultEncoding = oldDefaultEncoding ;
this . Name = longName . TrimEnd ( ) ;
this . ShortName = shortName . TrimEnd ( ) ;
}
#endregion
2022-11-22 21:36:01 +01:00
#region Export ( )
public void Export ( byte [ ] buffer , Encoding encoding )
{
Array . Copy ( this . data , this . FileOffset , buffer , 0 , this . Length + 1 ) ;
if ( ! this . IsNameModified )
return ;
// 43-50 + 115-126 in version 103 or 115-131 in version 105: channel name
2025-03-08 18:19:10 +01:00
if ( this . forceUtf8 )
encoding = Encoding . UTF8 ;
2022-11-22 21:36:01 +01:00
var bytes = encoding . GetBytes ( this . Name ) ;
Tools . MemSet ( buffer , 43 , 32 , 8 ) ;
Tools . MemSet ( buffer , 115 , 32 , buffer . Length - 115 - 1 ) ;
Array . Copy ( bytes , 0 , buffer , 43 , Math . Min ( bytes . Length , 8 ) ) ;
if ( bytes . Length > 8 )
Array . Copy ( bytes , 8 , buffer , 115 , Math . Min ( bytes . Length - 8 , this . Length - 115 - 1 ) ) ;
}
#endregion
2019-07-13 22:11:28 +02:00
}
}