2013-05-05 03:39:11 +02:00
//#define STORE_DVBS_CHANNELS_IN_DATABASE
2013-05-11 16:45:29 +02:00
//#define LM640T_EXPERIMENT
2013-04-03 12:47:24 +02:00
using System ;
2013-03-31 14:09:38 +02:00
using System.Collections.Generic ;
2021-07-24 18:59:03 +02:00
using System.Diagnostics ;
2013-03-31 14:09:38 +02:00
using System.IO ;
using System.Linq ;
2021-07-24 18:59:03 +02:00
using System.Reflection ;
2013-03-31 14:09:38 +02:00
using System.Text ;
using ChanSort.Api ;
2021-07-24 18:59:03 +02:00
using View = ChanSort . Api . View ;
2013-03-31 14:09:38 +02:00
2021-01-23 14:22:18 +01:00
namespace ChanSort.Loader.LG.Binary
2013-03-31 14:09:38 +02:00
{
2021-07-24 18:59:03 +02:00
public partial class TllFileSerializer : SerializerBase , ITllFileSerializer
2013-03-31 14:09:38 +02:00
{
2014-07-14 22:20:22 +02:00
enum SpecialHandlingModels { Standard , LH3000 , LH250 , PN , LP , LT , LY } ;
2013-10-07 16:32:16 +02:00
2013-04-06 01:46:28 +02:00
private const long DVBS_S2 = 0x0032532D53425644 ; // reverse of "DVBS-S2\0"
2013-04-03 12:47:24 +02:00
private const long MaxFileSize = 2000000 ;
private readonly string ERR_fileTooBig = Resource . TllFileSerializerPlugin_ERR_fileTooBig ;
private readonly string ERR_modelUnknown = Resource . TllFileSerializerPlugin_ERR_modelUnknown ;
2013-03-31 14:09:38 +02:00
private readonly string ERR_wrongChecksum = Resource . TllFileSerializer_ERR_wrongChecksum ;
private readonly string ERR_dupeChannel = Resource . TllFileSerializer_ERR_dupeChannel ;
2013-04-03 23:26:09 +02:00
private readonly Dictionary < int , DvbsDataLayout > satConfigs = new Dictionary < int , DvbsDataLayout > ( ) ;
2013-04-03 12:47:24 +02:00
private readonly MappingPool < DataMapping > actMappings = new MappingPool < DataMapping > ( "Analog and DVB-C/T" ) ;
2013-04-28 12:44:40 +02:00
private readonly MappingPool < DataMapping > dvbsMappings = new MappingPool < DataMapping > ( "DVB-S Channel" ) ;
private readonly MappingPool < DataMapping > dvbsTransponderMappings = new MappingPool < DataMapping > ( "DVB-S Transponder" ) ;
2013-04-03 12:47:24 +02:00
private readonly MappingPool < FirmwareData > firmwareMappings = new MappingPool < FirmwareData > ( "Firmware" ) ;
2013-07-19 17:27:02 +02:00
private readonly MappingPool < DataMapping > lnbMappings = new MappingPool < DataMapping > ( "LNB Config" ) ;
2013-04-03 23:26:09 +02:00
2021-09-05 04:36:56 +02:00
private readonly ChannelList avbcTvChannels = new ChannelList ( SignalSource . AnalogC | SignalSource . Tv , "Analog Cable" ) ;
private readonly ChannelList avbtTvChannels = new ChannelList ( SignalSource . AnalogT | SignalSource . Tv , "Analog Antenna" ) ;
2013-06-23 00:11:16 +02:00
private readonly ChannelList dvbcTvChannels = new ChannelList ( SignalSource . DvbC | SignalSource . Tv , "DVB-C TV" ) ;
private readonly ChannelList dvbtTvChannels = new ChannelList ( SignalSource . DvbT | SignalSource . Tv , "DVB-T TV" ) ;
private readonly ChannelList dvbcRadioChannels = new ChannelList ( SignalSource . DvbC | SignalSource . Radio , "DVB-C Radio" ) ;
private readonly ChannelList dvbtRadioChannels = new ChannelList ( SignalSource . DvbT | SignalSource . Radio , "DVB-T Radio" ) ;
private readonly ChannelList satTvChannels = new ChannelList ( SignalSource . DvbS | SignalSource . Tv , "Sat TV" ) ;
private readonly ChannelList satRadioChannels = new ChannelList ( SignalSource . DvbS | SignalSource . Radio , "Sat Radio" ) ;
2013-04-03 12:47:24 +02:00
private byte [ ] fileContent ;
2013-10-07 16:32:16 +02:00
private SpecialHandlingModels specialModel = SpecialHandlingModels . Standard ;
2013-04-03 12:47:24 +02:00
2013-03-31 14:09:38 +02:00
private int analogBlockOffset ;
private int firmwareBlockOffset ;
2014-07-14 22:20:22 +02:00
private int hospitalityBlockOffset ;
2013-03-31 14:09:38 +02:00
private int dvbctBlockOffset ;
private int dvbsBlockOffset ;
private int [ ] dvbsSubblockCrcOffset ;
private int settingsBlockOffset ;
2013-04-03 12:47:24 +02:00
private int actChannelSize ;
private bool reorderPhysically ;
private int analogChannelCount ;
private int dvbctChannelCount ;
private int dvbsChannelCount ;
private DvbsDataLayout satConfig ;
2014-01-19 19:08:17 +01:00
private Dictionary < int , ushort > nextChannelIndex ;
2013-04-03 12:47:24 +02:00
private int firmwareBlockSize ;
2014-07-14 22:20:22 +02:00
private int hospitalityBlockSize ;
2013-04-03 12:47:24 +02:00
private int dvbsBlockSize ;
private int settingsBlockSize ;
2013-03-31 14:09:38 +02:00
private string countryCode ;
2013-04-03 12:47:24 +02:00
private int duplicateChannels ;
private int deletedChannelsHard ;
private int deletedChannelsSoft ;
private int dvbsChannelsAtPr0 ;
2014-11-04 11:15:34 +01:00
private bool removeDeletedActChannels ;
private bool mustReorganizeDvbs ;
2013-05-11 16:45:29 +02:00
private decimal dvbsSymbolRateFactor ;
2013-03-31 14:09:38 +02:00
2021-07-24 18:59:03 +02:00
private ILgUserInterfaceFactory uiFactory ;
2013-03-31 14:09:38 +02:00
#region ctor ( )
2013-04-03 12:47:24 +02:00
public TllFileSerializer ( string inputFile ) : base ( inputFile )
{
2015-06-13 18:37:59 +02:00
this . Features . ChannelNameEdit = ChannelNameEditMode . Analog ;
2019-11-08 02:31:44 +01:00
this . Features . DeleteMode = DeleteMode . FlagWithoutPrNr ;
2019-11-24 20:00:48 +01:00
this . Features . CanSkipChannels = true ;
this . Features . CanLockChannels = true ;
this . Features . CanHideChannels = true ;
2019-11-08 02:31:44 +01:00
this . Features . CanHaveGaps = true ;
2013-04-03 12:47:24 +02:00
this . Features . DeviceSettings = true ;
2013-04-29 00:35:40 +02:00
this . Features . CleanUpChannelData = true ;
2021-03-14 22:13:22 +01:00
this . Features . FavoritesMode = FavoritesMode . Flags ;
this . Features . MaxFavoriteLists = 4 ;
2013-03-31 14:09:38 +02:00
this . SupportedTvCountryCodes = new List < string >
{
"___ (None)" , "AUT (Austria)" , "BEL (Belgium)" , "CHE (Switzerland)" ,
"DEU (Germany)" , "ESP (Spain)" , "FRA (France)" , "GBR (Great Britain)" ,
"GRC (Greece)" , "IRL (Ireland)" , "ITA (Italy)" , "LUX (Luxembourg)" ,
2013-04-03 12:47:24 +02:00
"NLD (Netherlands)" , "PRT (Portugal)" , "SVN (Slovenia)"
2013-03-31 14:09:38 +02:00
} ;
2013-04-03 12:47:24 +02:00
this . ReadConfigurationFromIniFile ( ) ;
2013-04-03 23:26:09 +02:00
2021-09-05 04:36:56 +02:00
this . DataRoot . AddChannelList ( avbcTvChannels ) ;
this . DataRoot . AddChannelList ( avbtTvChannels ) ;
2013-06-23 00:11:16 +02:00
this . DataRoot . AddChannelList ( dvbcTvChannels ) ;
this . DataRoot . AddChannelList ( dvbcRadioChannels ) ;
this . DataRoot . AddChannelList ( dvbtTvChannels ) ;
this . DataRoot . AddChannelList ( dvbtRadioChannels ) ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
public IList < string > SupportedTvCountryCodes { get ; private set ; }
#region ReadConfigurationFromIniFile ( )
private void ReadConfigurationFromIniFile ( )
2013-03-31 14:09:38 +02:00
{
2021-08-31 22:13:28 +02:00
string iniFile = this . GetType ( ) . Assembly . Location . ToLowerInvariant ( ) . Replace ( ".dll" , ".ini" ) ;
2013-04-03 12:47:24 +02:00
IniFile ini = new IniFile ( iniFile ) ;
foreach ( var section in ini . Sections )
{
int idx = section . Name . IndexOf ( ":" ) ;
2013-05-03 19:02:30 +02:00
string recordLength = idx < 0 ? "" : section . Name . Substring ( idx + 1 ) ;
2013-04-03 12:47:24 +02:00
if ( section . Name . StartsWith ( "DvbsBlock" ) )
2013-05-03 19:02:30 +02:00
this . satConfigs . Add ( int . Parse ( recordLength ) , new DvbsDataLayout ( section ) ) ;
2013-04-03 12:47:24 +02:00
else if ( section . Name . StartsWith ( "ACTChannelDataMapping" ) )
actMappings . AddMapping ( recordLength , new DataMapping ( section ) ) ;
else if ( section . Name . StartsWith ( "SatChannelDataMapping" ) )
dvbsMappings . AddMapping ( recordLength , new DataMapping ( section ) ) ;
2013-04-28 12:44:40 +02:00
else if ( section . Name . StartsWith ( "TransponderDataMapping" ) )
dvbsTransponderMappings . AddMapping ( recordLength , new DataMapping ( section ) ) ;
2013-07-19 17:27:02 +02:00
else if ( section . Name . StartsWith ( "LnbMapping" ) )
lnbMappings . AddMapping ( recordLength , new DataMapping ( section ) ) ;
2013-04-03 12:47:24 +02:00
else if ( section . Name . StartsWith ( "FirmwareData" ) )
firmwareMappings . AddMapping ( recordLength , new FirmwareData ( section ) ) ;
}
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
2013-03-31 14:09:38 +02:00
#region Load ( )
2013-04-03 12:47:24 +02:00
public override void Load ( )
2013-03-31 14:09:38 +02:00
{
2021-07-24 18:59:03 +02:00
this . InitUiFactory ( ) ;
2021-08-31 22:13:28 +02:00
string basename = ( Path . GetFileNameWithoutExtension ( this . FileName ) ? ? "" ) . ToUpperInvariant ( ) ;
2014-07-08 22:16:16 +02:00
if ( basename . StartsWith ( "XXLH250" ) )
this . specialModel = SpecialHandlingModels . LH250 ;
else if ( basename . StartsWith ( "XXLH3000" ) )
2013-10-07 16:32:16 +02:00
this . specialModel = SpecialHandlingModels . LH3000 ;
else if ( basename . StartsWith ( "XXPN" ) )
this . specialModel = SpecialHandlingModels . PN ;
else if ( basename . StartsWith ( "XXLP" ) )
this . specialModel = SpecialHandlingModels . LP ;
else if ( basename . StartsWith ( "XXLT" ) )
this . specialModel = SpecialHandlingModels . LT ;
2014-07-14 22:20:22 +02:00
else if ( basename . StartsWith ( "XXLY" ) )
this . specialModel = SpecialHandlingModels . LY ;
2013-10-07 16:32:16 +02:00
else
this . specialModel = SpecialHandlingModels . Standard ;
2013-04-03 12:47:24 +02:00
long fileSize = new FileInfo ( this . FileName ) . Length ;
if ( fileSize > MaxFileSize )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( string . Format ( ERR_fileTooBig , fileSize , MaxFileSize ) ) ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
this . fileContent = File . ReadAllBytes ( this . FileName ) ;
2019-11-11 16:06:11 +01:00
if ( this . fileContent [ 0 ] = = '<' )
2022-12-04 18:18:52 +01:00
throw LoaderException . TryNext ( "Invalid binary TLL file format. Maybe a GlobalClone/XML file?" ) ;
2019-11-11 16:06:11 +01:00
2013-04-03 12:47:24 +02:00
int off = 0 ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
this . ReadFileHeader ( ref off ) ;
this . ReadAnalogChannelBlock ( ref off ) ;
this . ReadFirmwareDataBlock ( ref off ) ;
2013-10-07 16:32:16 +02:00
this . ReadHotelModelExtraBlock ( ref off ) ;
2013-04-03 12:47:24 +02:00
this . ReadDvbCtChannels ( ref off ) ;
this . ReadDvbSBlock ( ref off ) ;
2013-05-16 21:06:44 +02:00
this . ReadSettingsBlock ( ref off ) ;
2013-05-11 16:45:29 +02:00
2013-11-12 18:25:15 +01:00
if ( this . satTvChannels . PresetProgramNrCount > 0 | | this . satRadioChannels . PresetProgramNrCount > 0 )
2013-05-16 21:06:44 +02:00
{
foreach ( var channel in this . satTvChannels . Channels )
channel . NewProgramNr = channel . OldProgramNr ;
foreach ( var channel in this . satRadioChannels . Channels )
channel . NewProgramNr = channel . OldProgramNr ;
2021-07-24 18:59:03 +02:00
if ( IsTesting | | ! PromptForEditingPresetList ( ) )
2013-11-12 18:25:15 +01:00
{
this . satTvChannels . ReadOnly = true ;
this . satRadioChannels . ReadOnly = true ;
}
2013-05-16 21:06:44 +02:00
}
2013-04-29 00:35:40 +02:00
2013-04-03 12:47:24 +02:00
#if STORE_DVBS_CHANNELS_IN_DATABASE
2013-03-31 14:09:38 +02:00
this . StoreToDatabase ( ) ;
2013-04-03 12:47:24 +02:00
#endif
2013-05-30 09:28:01 +02:00
foreach ( var list in this . DataRoot . ChannelLists )
list . MaxChannelNameLength = 40 ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
2021-07-24 18:59:03 +02:00
private void InitUiFactory ( )
{
if ( this . uiFactory ! = null | | this . IsTesting )
return ;
// run-time-binding to the UI factory so that the loader module can be edited + compiled without DevExpress dependencies
var ass = Assembly . Load ( new AssemblyName ( "ChanSort.Loader.LG.UI" ) ) ;
var type = ass . GetType ( "ChanSort.Loader.LG.UI.LgUserInterfaceFactory" ) ;
this . uiFactory = ( ILgUserInterfaceFactory ) Activator . CreateInstance ( type ) ;
}
private bool PromptForEditingPresetList ( )
{
#if false
return uiFactory . ShowPresetProgramNrDialog ( ) ;
#else
View . Default . ShowHtmlBox (
@ "<b>Editing of the satellite channel list is disabled!</b>
This file contains preset program numbers for satellite channels .
Due to issues with most recent LG firmwares such lists can no longer be modified reliably .
< b > To enable editing you must first run a clean full channel search : < / b >
- Keep a copy of the current TLL file if you want to use it as a reference list later
- Execute a Factory Reset on your TV
- Execute an automatic channel search with options ' Full ' , ' None ' and ' Blind search '
- Save the new list to USB and open it with ChanSort
< a href = ' http : //sourceforge.net/p/chansort/wiki/Channels%20disappear%20or%20change%20program%20numbers%20randomly/'>See the ChanSort Wiki for details</a>",
"!!! Attention !!!" , 450 , 220 , url = > Process . Start ( url ) ) ;
return false ; // TODO
#endif
}
2013-03-31 14:09:38 +02:00
#endregion
2013-04-03 12:47:24 +02:00
#region ReadFileHeader ( )
private void ReadFileHeader ( ref int off )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
if ( fileContent . Length < 4 )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( ERR_modelUnknown ) ;
2015-09-19 23:24:31 +02:00
var magic = BitConverter . ToUInt32 ( fileContent , off ) ;
if ( magic = = 0x5A5A5A5A | | magic = = 0xA5A5A5A5 )
2013-04-03 12:47:24 +02:00
off + = 4 ;
}
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region ReadAnalogChannelBlock ( )
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
private void ReadAnalogChannelBlock ( ref int off )
{
this . analogBlockOffset = off ;
2013-05-05 22:40:57 +02:00
this . ReadActChannelBlock ( ref off , out analogChannelCount , ref actChannelSize ,
2013-04-03 12:47:24 +02:00
( slot , data ) = > new AnalogChannel ( slot , data ) ) ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region ReadFirmwareDataBlock ( )
private void ReadFirmwareDataBlock ( ref int off )
{
this . firmwareBlockOffset = off ;
this . firmwareBlockSize = this . GetBlockSize ( off ) ;
off + = 4 + this . firmwareBlockSize ;
}
2013-03-31 14:09:38 +02:00
#endregion
2013-10-07 16:32:16 +02:00
#region ReadHotelModelExtraBlock ( )
private void ReadHotelModelExtraBlock ( ref int off )
2013-07-23 20:53:59 +02:00
{
2014-07-14 22:20:22 +02:00
if ( ! ( this . specialModel = = SpecialHandlingModels . LT | | this . specialModel = = SpecialHandlingModels . LP | | this . specialModel = = SpecialHandlingModels . LY ) )
2013-10-07 16:32:16 +02:00
return ;
2013-07-23 20:53:59 +02:00
int size = BitConverter . ToInt32 ( this . fileContent , off ) ;
2014-07-14 22:20:22 +02:00
this . hospitalityBlockOffset = off ;
this . hospitalityBlockSize = size ;
2013-10-07 16:32:16 +02:00
off + = 4 + size ;
2013-07-23 20:53:59 +02:00
}
#endregion
2013-03-31 14:09:38 +02:00
#region ReadDvbCtChannels ( )
2013-04-03 12:47:24 +02:00
private void ReadDvbCtChannels ( ref int off )
{
this . dvbctBlockOffset = off ;
2013-05-05 22:40:57 +02:00
this . ReadActChannelBlock ( ref off , out dvbctChannelCount , ref actChannelSize ,
2013-04-03 12:47:24 +02:00
( slot , data ) = > new DtvChannel ( slot , data ) ) ;
}
#endregion
#region ReadDvbSBlock ( )
private void ReadDvbSBlock ( ref int off )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
int blockSize ;
2013-04-06 01:46:28 +02:00
if ( ! IsDvbsBlock ( ref off , out blockSize ) )
2013-04-03 12:47:24 +02:00
return ;
this . dvbsBlockSize = blockSize ;
this . dvbsBlockOffset = off ;
off + = 4 ;
this . satConfig = this . satConfigs . TryGet ( blockSize ) ;
if ( satConfig ! = null )
this . ReadDvbsSubblocks ( ref off ) ;
else
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
this . DataRoot . Warnings . AppendFormat ( "DVB-S data format is not supported (size={0})\n" , blockSize ) ;
off + = blockSize ;
}
}
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region ReadSettingsBlock ( )
private void ReadSettingsBlock ( ref int off )
{
this . settingsBlockOffset = off ;
if ( this . settingsBlockOffset > = fileContent . Length )
{
this . settingsBlockOffset = 0 ;
return ;
}
this . settingsBlockSize = this . GetBlockSize ( off ) ;
off + = 4 ;
if ( settingsBlockSize > = 8 )
{
StringBuilder code = new StringBuilder ( ) ;
for ( int i = 6 ; i > = 4 ; i - - )
code . Append ( ( char ) fileContent [ off + i ] ) ;
this . countryCode = code . ToString ( ) ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
off + = settingsBlockSize ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
#region ReadActChannelBlock ( )
2013-05-05 22:40:57 +02:00
private void ReadActChannelBlock ( ref int off , out int channelCount , ref int recordSize ,
2013-04-03 12:47:24 +02:00
Func < int , DataMapping , ChannelInfo > channelFactory )
2013-03-31 14:09:38 +02:00
{
2014-11-04 11:15:34 +01:00
int blockSize = this . GetBlockSize ( off , 2 ) ;
2013-04-03 12:47:24 +02:00
off + = 4 ;
2013-10-07 16:32:16 +02:00
2013-04-03 12:47:24 +02:00
channelCount = BitConverter . ToInt32 ( fileContent , off ) ;
off + = 4 ;
if ( channelCount = = 0 ) return ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
recordSize = GetActChannelRecordSize ( off , blockSize , channelCount ) ;
2013-10-07 16:32:16 +02:00
var actMapping = GetActChannelMapping ( recordSize ) ;
2013-04-03 12:47:24 +02:00
this . reorderPhysically = actMapping . Settings . GetInt ( "reorderChannelData" ) ! = 0 ;
2014-11-04 11:15:34 +01:00
this . removeDeletedActChannels | = this . reorderPhysically ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
for ( int i = 0 ; i < channelCount ; i + + )
{
actMapping . SetDataPtr ( fileContent , off ) ;
ChannelInfo ci = channelFactory ( i , actMapping ) ;
2013-03-31 14:09:38 +02:00
2013-04-03 23:26:09 +02:00
var list = this . DataRoot . GetChannelList ( ci . SignalSource ) ;
2013-04-03 12:47:24 +02:00
this . DataRoot . AddChannel ( list , ci ) ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
off + = recordSize ;
}
}
2013-10-07 16:32:16 +02:00
#endregion
#region GetActChannelMapping ( )
private DataMapping GetActChannelMapping ( int recordSize )
{
var key = recordSize . ToString ( ) ;
2014-07-08 22:16:16 +02:00
if ( this . specialModel = = SpecialHandlingModels . LH250 )
key + = "LH250" ;
else if ( this . specialModel = = SpecialHandlingModels . LH3000 )
2013-10-07 16:32:16 +02:00
key + = "LH3000" ;
else if ( this . specialModel = = SpecialHandlingModels . PN )
key + = "PN" ;
2014-07-14 22:20:22 +02:00
else if ( this . specialModel = = SpecialHandlingModels . LY )
key + = "LY" ;
2013-10-07 16:32:16 +02:00
var actMapping = this . actMappings . GetMapping ( key ) ;
return actMapping ;
}
2013-04-03 12:47:24 +02:00
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region GetBlockSize ( )
private int GetBlockSize ( int off , int minSize = 0 )
{
long len = BitConverter . ToUInt32 ( fileContent , off ) ;
if ( len < minSize | | off + 4 + len > fileContent . Length )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( ERR_modelUnknown ) ;
2013-04-03 12:47:24 +02:00
return ( int ) len ;
}
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region GetActChannelRecordSize ( )
private int GetActChannelRecordSize ( int off , int blockSize , int channelCount )
{
if ( ( blockSize - 4 ) % channelCount ! = 0 )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( ERR_modelUnknown ) ;
2013-04-03 12:47:24 +02:00
int recordSize = ( blockSize - 4 ) / channelCount ;
if ( off + channelCount * recordSize > fileContent . Length )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( ERR_modelUnknown ) ;
2013-04-03 12:47:24 +02:00
return recordSize ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region IsDvbsBlock ( )
2013-04-06 01:46:28 +02:00
private bool IsDvbsBlock ( ref int off , out int blockSize )
2013-04-03 12:47:24 +02:00
{
blockSize = 0 ;
if ( off > = fileContent . Length )
return false ;
blockSize = this . GetBlockSize ( off ) ;
if ( blockSize < 12 )
return false ;
2013-04-06 01:46:28 +02:00
long blockId = BitConverter . ToInt64 ( fileContent , off + 8 ) ;
if ( blockId = = DVBS_S2 )
return true ;
2013-05-30 09:28:01 +02:00
// LW4500, LW5400, LMxxxT and maybe other models with bogus DVB-S block
2013-04-06 01:46:28 +02:00
if ( blockId = = - 1 )
{
this . satConfig = satConfigs . TryGet ( blockSize ) ;
if ( this . satConfig ! = null )
{
2013-05-30 09:28:01 +02:00
this . dvbsBlockSize = blockSize ;
this . dvbsBlockOffset = off ;
off + = 4 + blockSize ;
2013-04-06 01:46:28 +02:00
}
}
2013-05-30 09:28:01 +02:00
2013-04-06 01:46:28 +02:00
return false ;
2013-04-03 12:47:24 +02:00
}
2013-03-31 14:09:38 +02:00
#endregion
2013-04-03 12:47:24 +02:00
#region ReadDvbsSubblocks ( )
private void ReadDvbsSubblocks ( ref int off )
2013-03-31 14:09:38 +02:00
{
2013-04-06 01:46:28 +02:00
this . DataRoot . AddChannelList ( satTvChannels ) ;
this . DataRoot . AddChannelList ( satRadioChannels ) ;
2013-04-08 10:08:49 +02:00
this . VerifyDvbsSubblockChecksums ( off ) ;
2013-04-03 12:47:24 +02:00
// subblock 1 (DVBS header)
off + = 16 ;
// subblock 2 (satellites)
off + = 84 ; // irrelevant data
this . ReadSatellites ( ref off ) ;
// subblock 3 (transponder)
off + = satConfig . sizeOfTransponderBlockHeader ;
this . ReadTransponderData ( ref off ) ;
// subblock 4 (channels)
SatChannelListHeader header = new SatChannelListHeader ( fileContent , off ) ;
this . dvbsChannelCount = header . ChannelCount ;
off + = header . Size ;
off + = satConfig . dvbsMaxChannelCount / 8 ; // skip allocation bitmap
2013-04-29 00:35:40 +02:00
this . ReadDvbsChannelLinkedList ( header , ref off ) ;
2013-05-07 12:20:38 +02:00
off + = satConfig . linkedListExtraDataLength ;
2013-04-08 10:08:49 +02:00
this . ReadDvbsChannels ( ref off , header . LinkedListStartIndex ) ;
2013-04-03 12:47:24 +02:00
// subblock 5 (satellite/LNB config)
2013-07-19 17:27:02 +02:00
this . ReadLnbConfig ( ref off ) ;
2013-04-03 12:47:24 +02:00
}
#endregion
2013-04-08 10:08:49 +02:00
#region VerifyDvbsSubblockChecksums ( )
private void VerifyDvbsSubblockChecksums ( int off )
2013-04-03 12:47:24 +02:00
{
this . dvbsSubblockCrcOffset = new int [ satConfig . dvbsSubblockLength . Length ] ;
2013-03-31 14:09:38 +02:00
for ( int i = 0 ; i < dvbsSubblockCrcOffset . Length ; i + + )
{
2013-04-03 12:47:24 +02:00
this . dvbsSubblockCrcOffset [ i ] = off ;
int subblockLength = satConfig . dvbsSubblockLength [ i ] ;
uint fileCrc = BitConverter . ToUInt32 ( fileContent , off ) ;
2019-07-20 02:08:51 +02:00
uint calcCrc = Crc32 . Reversed . CalcCrc32 ( fileContent , off + 4 , subblockLength ) ;
2013-03-31 14:09:38 +02:00
if ( fileCrc ! = calcCrc )
2022-11-29 14:56:23 +01:00
throw LoaderException . Fail ( string . Format ( ERR_wrongChecksum , fileCrc , calcCrc ) ) ;
2013-04-03 12:47:24 +02:00
off + = 4 + subblockLength ;
2013-03-31 14:09:38 +02:00
}
}
#endregion
#region ReadSatellites ( )
2013-04-03 12:47:24 +02:00
private void ReadSatellites ( ref int off )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
for ( int i = 0 ; i < satConfig . satCount ; i + + )
2013-03-31 14:09:38 +02:00
{
Satellite sat = new Satellite ( i ) ;
2013-04-03 12:47:24 +02:00
string satName = Encoding . ASCII . GetString ( fileContent , off + 0 , 32 ) . TrimEnd ( '\0' ) ;
sat . Name = satName ;
sat . OrbitalPosition = GetSatLocation ( fileContent [ off + 32 ] , fileContent [ off + 33 ] ) ;
2013-03-31 14:09:38 +02:00
this . DataRoot . AddSatellite ( sat ) ;
2013-04-03 12:47:24 +02:00
off + = satConfig . satLength ;
2013-03-31 14:09:38 +02:00
}
}
#endregion
#region ReadTransponderData ( )
2013-04-03 12:47:24 +02:00
private void ReadTransponderData ( ref int off )
2013-03-31 14:09:38 +02:00
{
2013-05-11 16:45:29 +02:00
dvbsSymbolRateFactor = 1 ;
var dvbsSymbolRateMask = 0x7FFFF ;
2013-10-07 16:32:16 +02:00
var mapping = GetTransponderMapping ( ) ;
2013-04-03 12:47:24 +02:00
for ( int i = 0 ; i < satConfig . transponderCount ; i + + )
2013-03-31 14:09:38 +02:00
{
2013-04-28 12:44:40 +02:00
mapping . SetDataPtr ( this . fileContent , off + i * satConfig . transponderLength ) ;
2013-10-07 16:32:16 +02:00
SatTransponder transponder = new SatTransponder ( i , mapping , this . DataRoot , this . satConfig . satIndexFactor ) ;
2013-04-29 00:35:40 +02:00
if ( transponder . Satellite = = null )
2013-03-31 14:09:38 +02:00
continue ;
2013-05-11 16:45:29 +02:00
if ( ( transponder . SymbolRate & 0x7FFF ) = = 11000 )
dvbsSymbolRateFactor = 2 ;
else if ( transponder . SymbolRate = = 44000 )
{
dvbsSymbolRateMask = 0xFFFF ;
dvbsSymbolRateFactor = 0.5 m ;
}
2013-03-31 14:09:38 +02:00
2013-04-29 00:35:40 +02:00
var sat = transponder . Satellite ;
2013-03-31 14:09:38 +02:00
this . DataRoot . AddTransponder ( sat , transponder ) ;
}
2013-04-03 12:47:24 +02:00
2013-05-11 16:45:29 +02:00
if ( dvbsSymbolRateFactor ! = 1 )
2013-04-03 12:47:24 +02:00
{
foreach ( var transponder in this . DataRoot . Transponder . Values )
2013-05-11 16:45:29 +02:00
{
int symbolRate = transponder . SymbolRate & dvbsSymbolRateMask ;
transponder . SymbolRate = ( int ) ( symbolRate * dvbsSymbolRateFactor ) ;
}
2013-04-03 12:47:24 +02:00
}
off + = this . satConfig . transponderCount * this . satConfig . transponderLength ;
2013-03-31 14:09:38 +02:00
}
2013-10-07 16:32:16 +02:00
private DataMapping GetTransponderMapping ( )
{
string key = this . satConfig . transponderLength . ToString ( ) ;
if ( this . specialModel = = SpecialHandlingModels . LP )
key + = "LP" ;
var mapping = this . dvbsTransponderMappings . GetMapping ( key ) ;
return mapping ;
}
2013-03-31 14:09:38 +02:00
#endregion
#region ReadDvbsChannelLinkedList ( )
2013-04-29 00:35:40 +02:00
private void ReadDvbsChannelLinkedList ( SatChannelListHeader header , ref int off )
2013-03-31 14:09:38 +02:00
{
2014-01-19 19:08:17 +01:00
this . nextChannelIndex = new Dictionary < int , ushort > ( ) ;
2013-04-29 00:35:40 +02:00
int index = header . LinkedListStartIndex ;
while ( index ! = 0xFFFF )
2013-03-31 14:09:38 +02:00
{
2013-04-29 00:35:40 +02:00
int offEntry = off + index * satConfig . sizeOfChannelLinkedListEntry ;
2014-01-19 19:08:17 +01:00
ushort nextIndex = BitConverter . ToUInt16 ( fileContent , offEntry + 2 ) ;
2013-05-07 00:27:17 +02:00
if ( this . nextChannelIndex . ContainsKey ( index ) ) // prevent infinite loop (exists in some test files)
break ;
2013-04-29 00:35:40 +02:00
this . nextChannelIndex . Add ( index , nextIndex ) ;
index = nextIndex ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
off + = satConfig . dvbsMaxChannelCount * satConfig . sizeOfChannelLinkedListEntry ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-08 10:08:49 +02:00
#region ReadDvbsChannels ( )
2014-01-19 19:08:17 +01:00
private void ReadDvbsChannels ( ref int off , ushort startIndex )
2013-03-31 14:09:38 +02:00
{
2013-10-07 16:32:16 +02:00
var mapping = GetDvbsChannelMapping ( ) ;
2014-01-19 19:08:17 +01:00
ushort index = startIndex ;
2013-03-31 14:09:38 +02:00
for ( int i = 0 ; i < this . dvbsChannelCount ; i + + )
{
2013-04-03 12:47:24 +02:00
int recordOffset = off + index * satConfig . dvbsChannelLength ;
mapping . SetDataPtr ( fileContent , recordOffset ) ;
SatChannel ci = new SatChannel ( i , index , mapping , this . DataRoot ) ;
if ( ! ci . InUse )
+ + this . deletedChannelsHard ;
else
2013-03-31 14:09:38 +02:00
{
2013-04-29 00:35:40 +02:00
if ( ci . IsDeleted )
+ + this . deletedChannelsSoft ;
2013-04-03 23:26:09 +02:00
var list = this . DataRoot . GetChannelList ( ci . SignalSource ) ;
2013-04-03 12:47:24 +02:00
var dupes = list . GetChannelByUid ( ci . Uid ) ;
if ( dupes . Count = = 0 )
{
2013-04-29 00:35:40 +02:00
if ( ci . OldProgramNr = = 0 & & ! ci . IsDeleted )
2013-04-03 12:47:24 +02:00
+ + this . dvbsChannelsAtPr0 ;
}
2013-03-31 14:09:38 +02:00
else
2013-04-03 12:47:24 +02:00
{
this . DataRoot . Warnings . AppendFormat ( ERR_dupeChannel , ci . RecordIndex , ci . OldProgramNr , dupes [ 0 ] . RecordIndex ,
dupes [ 0 ] . OldProgramNr , dupes [ 0 ] . Name ) . AppendLine ( ) ;
+ + this . duplicateChannels ;
}
2013-04-29 00:35:40 +02:00
this . DataRoot . AddChannel ( list , ci ) ;
2013-03-31 14:09:38 +02:00
}
2014-01-19 19:08:17 +01:00
if ( ! this . nextChannelIndex . TryGetValue ( index , out index ) | | index = = 0xFFFF )
2013-03-31 14:09:38 +02:00
break ;
}
2013-04-03 12:47:24 +02:00
off + = satConfig . dvbsMaxChannelCount * satConfig . dvbsChannelLength ;
2013-03-31 14:09:38 +02:00
}
2013-10-07 16:32:16 +02:00
#endregion
#region GetDvbsChannelMapping ( )
private DataMapping GetDvbsChannelMapping ( )
{
var key = satConfig . dvbsChannelLength . ToString ( ) ;
if ( this . specialModel = = SpecialHandlingModels . LP )
key + = "LP" ;
var mapping = this . dvbsMappings . GetMapping ( key ) ;
return mapping ;
}
2013-03-31 14:09:38 +02:00
#endregion
#region GetSatLocation ( )
2013-04-03 12:47:24 +02:00
private string GetSatLocation ( byte degree , byte fractionAndOrientation )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
return string . Format ( "{0}.{1}{2}" , degree , fractionAndOrientation & 0x0f , fractionAndOrientation < 16 ? "W" : "E" ) ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-07-19 17:27:02 +02:00
#region ReadLnbConfig ( )
private void ReadLnbConfig ( ref int off )
{
off + = satConfig . LnbBlockHeaderSize ;
var mapping = this . lnbMappings . GetMapping ( satConfig . lnbLength ) ;
for ( int i = 0 ; i < satConfig . lnbCount ; i + + )
{
mapping . SetDataPtr ( this . fileContent , off ) ;
var lnb = new LnbConfig ( mapping , this . DataRoot ) ;
if ( lnb . Id ! = 0 )
{
lnb . Satellite . LnbConfig = lnb ;
this . DataRoot . AddLnbConfig ( lnb ) ;
}
off + = satConfig . lnbLength ;
}
}
#endregion
2013-04-29 00:35:40 +02:00
// Test code for fixing broken DVB-S block of xxLM640T ==========
2013-03-31 14:09:38 +02:00
2013-04-06 01:46:28 +02:00
#region EraseDvbsBlock ( )
2013-05-11 16:45:29 +02:00
#if LM640T_EXPERIMENT
2013-04-06 01:46:28 +02:00
/// <summary>
/// The model LM640T has the whole DVB-S2 block filled with 0xFF bytes, including the checksums.
/// When a file (even the originally saved one) is loaded back, the TV crashes and performs a factory reset.
/// </summary>
private void EraseDvbsBlock ( int off )
{
this . dvbsBlockOffset = off ;
this . dvbsBlockSize = satConfig . dvbsBlockTotalLength ;
this . dvbsSubblockCrcOffset = new int [ satConfig . dvbsSubblockLength . Length ] ;
int p = this . dvbsBlockOffset + 4 ;
for ( int i = 0 ; i < this . dvbsBlockSize ; i + + )
this . fileContent [ p + + ] = 0 ;
using ( MemoryStream stream = new MemoryStream ( this . fileContent ) )
using ( BinaryWriter wrt = new BinaryWriter ( stream ) )
{
stream . Seek ( this . dvbsBlockOffset + 4 , SeekOrigin . Begin ) ; // skip length
// header
this . dvbsSubblockCrcOffset [ 0 ] = ( int ) stream . Position ;
stream . Seek ( 4 , SeekOrigin . Current ) ; // skip CRC32
stream . Write ( Encoding . ASCII . GetBytes ( "DVBS-S2\0" ) , 0 , 8 ) ;
wrt . Write ( ( ushort ) 7 ) ;
wrt . Write ( ( ushort ) 4 ) ;
2013-05-11 16:45:29 +02:00
2013-04-06 01:46:28 +02:00
// satellite
this . dvbsSubblockCrcOffset [ 1 ] = ( int ) stream . Position ;
stream . Seek ( 4 , SeekOrigin . Current ) ; // skip CRC32
stream . Seek ( 2 + satConfig . satCount / 8 + 2 + 2 + satConfig . satCount + 2 , SeekOrigin . Current ) ;
for ( int i = 0 ; i < satConfig . satCount ; i + + )
{
stream . Seek ( 36 , SeekOrigin . Current ) ;
wrt . Write ( ( short ) - 1 ) ;
wrt . Write ( ( short ) - 1 ) ;
stream . Seek ( 2 + 2 , SeekOrigin . Current ) ;
}
// transponders
this . dvbsSubblockCrcOffset [ 2 ] = ( int ) stream . Position ;
stream . Seek ( 4 , SeekOrigin . Current ) ; // skip CRC32
stream . Seek ( 5 * 2 + satConfig . transponderCount / 8 , SeekOrigin . Current ) ;
wrt . Write ( ( short ) - 1 ) ;
wrt . Write ( ( short ) - 1 ) ;
stream . Seek ( 2 + ( satConfig . transponderCount - 1 ) * 6 + 2 , SeekOrigin . Current ) ;
for ( int i = 0 ; i < satConfig . transponderCount ; i + + )
{
wrt . Write ( - 1 ) ;
wrt . Write ( ( ushort ) 0 ) ;
wrt . Write ( ( short ) - 1 ) ;
wrt . Write ( ( byte ) 0xfe ) ;
for ( int j = 9 ; j < satConfig . transponderLength ; j + + )
wrt . Write ( ( byte ) 0xFF ) ;
}
// channels
this . dvbsSubblockCrcOffset [ 3 ] = ( int ) stream . Position ;
stream . Seek ( 4 , SeekOrigin . Current ) ; // skip CRC32
stream . Seek ( 12 + satConfig . dvbsMaxChannelCount / 8 , SeekOrigin . Current ) ;
wrt . Write ( ( short ) - 1 ) ;
wrt . Write ( ( short ) - 1 ) ;
2013-05-11 16:45:29 +02:00
stream . Seek ( 4 + ( satConfig . dvbsMaxChannelCount - 1 ) * 8 + satConfig . linkedListExtraDataLength , SeekOrigin . Current ) ;
2013-04-06 01:46:28 +02:00
for ( int i = 0 ; i < satConfig . dvbsMaxChannelCount * satConfig . dvbsChannelLength ; i + + )
wrt . Write ( ( byte ) 0xFF ) ;
// sat/LNB-config
this . dvbsSubblockCrcOffset [ 4 ] = ( int ) stream . Position ;
stream . Seek ( 4 , SeekOrigin . Current ) ; // skip CRC32
stream . Seek ( 2 , SeekOrigin . Current ) ;
wrt . Write ( ( byte ) 1 ) ;
stream . Seek ( satConfig . lnbCount / 8 - 1 , SeekOrigin . Current ) ;
}
2013-05-11 16:45:29 +02:00
this . UpdateDvbsChecksums ( ) ;
2013-04-06 01:46:28 +02:00
}
2013-05-11 16:45:29 +02:00
#endif
2013-04-06 01:46:28 +02:00
#endregion
2013-04-29 00:35:40 +02:00
// Sat channel list cleanup ==================
#region CleanUpChannelData ( )
public override string CleanUpChannelData ( )
{
2013-05-30 09:28:01 +02:00
if ( this . satConfig = = null | | this . dvbsSubblockCrcOffset = = null )
2013-05-03 19:02:30 +02:00
return "" ;
2013-05-11 16:45:29 +02:00
this . DataRoot . NeedsSaving = true ;
2013-04-29 00:35:40 +02:00
this . ResetChannelInformationInTransponderData ( ) ;
byte [ ] sortedChannels = new byte [ this . satConfig . dvbsMaxChannelCount * this . satConfig . dvbsChannelLength ] ;
var channelsByTransponder =
this . satTvChannels . Channels . Union ( this . satRadioChannels . Channels ) . OrderBy ( PhysicalChannelOrder ) . ToList ( ) ;
2013-05-11 16:45:29 +02:00
int originalChannelCount = this . dvbsChannelCount ;
2013-04-29 00:35:40 +02:00
int prevChannelOrderId = - 1 ;
int prevTransponderIndex = - 1 ;
int channelCounter = 0 ;
int removedCounter = 0 ;
SatTransponder currentTransponder = null ;
foreach ( var channel in channelsByTransponder )
{
SatChannel satChannel = channel as SatChannel ;
if ( satChannel = = null ) // ignore proxy channels created by a reference list
continue ;
RelocateChannelData ( satChannel , ref prevChannelOrderId , sortedChannels , ref removedCounter ,
ref prevTransponderIndex , ref channelCounter , ref currentTransponder ) ;
}
if ( currentTransponder ! = null )
{
currentTransponder . LastChannelIndex = channelCounter - 1 ;
currentTransponder . ChannelCount = channelCounter - currentTransponder . FirstChannelIndex ;
}
// copy temp data back to fileContent and clear remainder
2013-06-27 20:21:56 +02:00
if ( originalChannelCount = = 0 )
{
// even if there's 0 channels, channel[0] must contain valid data
Tools . MemSet ( this . fileContent ,
2013-07-19 17:27:02 +02:00
this . dvbsBlockOffset + satConfig . ChannelListOffset + 1 * satConfig . dvbsChannelLength ,
2013-06-27 20:21:56 +02:00
0xFF ,
2013-07-19 17:27:02 +02:00
( satConfig . dvbsMaxChannelCount - 1 ) * satConfig . dvbsChannelLength ) ;
2013-06-27 20:21:56 +02:00
}
else
2013-05-11 16:45:29 +02:00
{
Tools . MemCopy ( sortedChannels , 0 ,
2013-07-19 17:27:02 +02:00
this . fileContent , this . dvbsBlockOffset + satConfig . ChannelListOffset ,
channelCounter * satConfig . dvbsChannelLength ) ;
2013-05-11 16:45:29 +02:00
Tools . MemSet ( this . fileContent ,
2013-07-19 17:27:02 +02:00
this . dvbsBlockOffset + satConfig . ChannelListOffset + channelCounter * satConfig . dvbsChannelLength ,
2013-05-11 16:45:29 +02:00
0xFF ,
2013-07-19 17:27:02 +02:00
( satConfig . dvbsMaxChannelCount - channelCounter ) * satConfig . dvbsChannelLength ) ;
2013-05-11 16:45:29 +02:00
}
2013-07-19 17:27:02 +02:00
2013-04-29 00:35:40 +02:00
UpdateChannelAllocationBitmap ( channelCounter ) ;
2013-05-11 16:45:29 +02:00
UpdateChannelLinkedList ( channelCounter ) ;
UpdateDvbsChecksums ( ) ;
2013-04-29 00:35:40 +02:00
return string . Format ( "{0} duplicate channels were detected and removed" , removedCounter ) ;
}
#endregion
#region ResetChannelInformationInTransponderData ( )
private void ResetChannelInformationInTransponderData ( )
{
foreach ( SatTransponder transponder in this . DataRoot . Transponder . Values )
{
transponder . FirstChannelIndex = 0xFFFF ;
transponder . LastChannelIndex = 0xFFFF ;
transponder . ChannelCount = 0 ;
}
}
#endregion
#region PhysicalChannelOrder ( )
private int PhysicalChannelOrder ( ChannelInfo channel )
{
return ( channel . Transponder . Id < < 16 ) + channel . ServiceId ;
}
#endregion
#region RelocateChannelData ( )
private void RelocateChannelData ( SatChannel channel , ref int prevChannelOrderId ,
byte [ ] sortedChannels , ref int removed , ref int prevTransponderIndex , ref int counter ,
ref SatTransponder currentTransponder )
{
if ( RemoveChannelIfDupe ( channel , ref prevChannelOrderId , ref removed ) )
return ;
UpdateChannelIndexInTransponderData ( channel , ref prevTransponderIndex , counter , ref currentTransponder ) ;
2013-07-19 17:27:02 +02:00
Tools . MemCopy (
channel . RawDataBuffer ,
channel . RawDataOffset ,
sortedChannels ,
counter * satConfig . dvbsChannelLength ,
satConfig . dvbsChannelLength ) ;
2013-04-29 00:35:40 +02:00
channel . RecordIndex = counter + + ;
2015-04-17 21:59:44 +02:00
channel . baseOffset = this . dvbsBlockOffset + satConfig . ChannelListOffset + ( int ) channel . RecordIndex * satConfig . dvbsChannelLength ;
2013-04-29 00:35:40 +02:00
}
#endregion
#region RemoveChannelIfDupe ( )
private bool RemoveChannelIfDupe ( SatChannel channel , ref int prevOrder , ref int removed )
{
int order = this . PhysicalChannelOrder ( channel ) ;
if ( order = = prevOrder )
{
var list = this . DataRoot . GetChannelList ( channel . SignalSource ) ;
list . RemoveChannel ( channel ) ;
+ + removed ;
channel . NewProgramNr = - 1 ;
channel . OldProgramNr = - 1 ;
return true ;
}
prevOrder = order ;
return false ;
}
#endregion
#region UpdateChannelIndexInTransponderData ( )
private void UpdateChannelIndexInTransponderData ( SatChannel channel , ref int prevTransponderIndex , int counter ,
ref SatTransponder transponder )
{
if ( channel . Transponder . Id = = prevTransponderIndex )
return ;
if ( transponder ! = null )
{
transponder . LastChannelIndex = counter - 1 ;
transponder . ChannelCount = counter - transponder . FirstChannelIndex ;
}
transponder = ( SatTransponder ) channel . Transponder ;
transponder . FirstChannelIndex = counter ;
prevTransponderIndex = channel . Transponder . Id ;
}
#endregion
#region UpdateChannelAllocationBitmap ( )
private void UpdateChannelAllocationBitmap ( int counter )
{
Tools . MemSet ( fileContent , this . dvbsBlockOffset + satConfig . AllocationBitmapOffset , 0 , satConfig . dvbsMaxChannelCount / 8 ) ;
Tools . MemSet ( fileContent , this . dvbsBlockOffset + satConfig . AllocationBitmapOffset , 0xFF , counter / 8 ) ;
if ( counter % 8 ! = 0 )
fileContent [ this . dvbsBlockOffset + satConfig . AllocationBitmapOffset + counter / 8 ] = ( byte ) ( 0xFF > > ( 8 - counter % 8 ) ) ;
}
#endregion
#region UpdateChannelLinkedList ( )
private void UpdateChannelLinkedList ( int counter )
{
var header = new SatChannelListHeader ( this . fileContent , this . dvbsBlockOffset + satConfig . ChannelListHeaderOffset ) ;
2013-05-07 12:20:38 +02:00
header . ChannelCount = counter ;
2013-04-29 00:35:40 +02:00
header . LinkedListStartIndex = 0 ;
2013-05-07 12:20:38 +02:00
if ( counter = = 0 )
counter = 1 ;
2013-04-29 00:35:40 +02:00
header . LinkedListEndIndex1 = counter - 1 ;
header . LinkedListEndIndex2 = counter - 1 ;
// update linked list
var off = this . dvbsBlockOffset + satConfig . SequenceTableOffset ;
for ( int i = 0 ; i < counter ; i + + )
{
Tools . SetInt16 ( this . fileContent , off + 0 , i - 1 ) ;
Tools . SetInt16 ( this . fileContent , off + 2 , i + 1 ) ;
Tools . SetInt16 ( this . fileContent , off + 4 , i ) ;
off + = satConfig . sizeOfChannelLinkedListEntry ;
}
Tools . SetInt16 ( this . fileContent , off - satConfig . sizeOfChannelLinkedListEntry + 2 , 0xFFFF ) ;
Tools . MemSet ( fileContent , off , 0 , ( satConfig . dvbsMaxChannelCount - counter ) * satConfig . sizeOfChannelLinkedListEntry ) ;
}
#endregion
// Saving ====================================
2013-04-06 01:46:28 +02:00
2013-03-31 14:09:38 +02:00
#region Save ( )
2022-11-29 22:00:16 +01:00
public override void Save ( )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
int newAnalogChannelCount ;
int newDvbctChannelCount ;
this . UpdateRawChannelData ( out newAnalogChannelCount , out newDvbctChannelCount ) ;
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
if ( ! removeDeletedActChannels )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
newAnalogChannelCount = this . analogChannelCount ;
newDvbctChannelCount = this . dvbctChannelCount ;
2013-03-31 14:09:38 +02:00
}
2013-04-28 12:44:40 +02:00
if ( this . reorderPhysically | | removeDeletedActChannels )
2013-04-03 12:47:24 +02:00
this . ReorderActChannelsPhysically ( ) ;
2013-03-31 14:09:38 +02:00
2013-07-19 17:27:02 +02:00
if ( this . mustReorganizeDvbs )
{
this . CleanUpChannelData ( ) ;
this . mustReorganizeDvbs = false ;
}
2013-05-30 09:28:01 +02:00
if ( this . dvbsSubblockCrcOffset ! = null )
2013-04-03 12:47:24 +02:00
this . UpdateDvbsChecksums ( ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
using var file = new BinaryWriter ( new FileStream ( this . FileName , FileMode . Create , FileAccess . Write ) ) ;
// header
file . Write ( this . fileContent , 0 , this . analogBlockOffset ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
// analog
file . Write ( newAnalogChannelCount * this . actChannelSize + 4 ) ;
file . Write ( newAnalogChannelCount ) ;
file . Write ( fileContent , this . analogBlockOffset + 8 , newAnalogChannelCount * this . actChannelSize ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
// firmware
file . Write ( fileContent , this . firmwareBlockOffset , this . firmwareBlockSize + 4 ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
// hospitality models extra block
if ( hospitalityBlockOffset ! = 0 )
file . Write ( fileContent , this . hospitalityBlockOffset , this . hospitalityBlockSize + 4 ) ;
2013-07-23 20:53:59 +02:00
2022-11-29 22:00:16 +01:00
// DVB-CT
file . Write ( newDvbctChannelCount * this . actChannelSize + 4 ) ;
file . Write ( newDvbctChannelCount ) ;
file . Write ( fileContent , this . dvbctBlockOffset + 8 , newDvbctChannelCount * this . actChannelSize ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
// DVB-S
if ( this . dvbsBlockOffset ! = 0 )
file . Write ( fileContent , this . dvbsBlockOffset , this . dvbsBlockSize + 4 ) ;
2013-03-31 14:09:38 +02:00
2022-11-29 22:00:16 +01:00
// rest (including settings)
if ( this . settingsBlockOffset ! = 0 )
file . Write ( fileContent , this . settingsBlockOffset , fileContent . Length - this . settingsBlockOffset ) ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
#region UpdateRawChannelData ( )
private void UpdateRawChannelData ( out int newAnalogChannelCount , out int newDvbctChannelCount )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
newAnalogChannelCount = 0 ;
newDvbctChannelCount = 0 ;
foreach ( var list in this . DataRoot . ChannelLists )
2013-03-31 14:09:38 +02:00
{
2019-11-08 02:31:44 +01:00
foreach ( var channel in list . Channels )
2013-03-31 14:09:38 +02:00
{
2019-11-08 02:31:44 +01:00
if ( channel . IsProxy )
continue ;
2013-04-10 00:35:25 +02:00
if ( channel . NewProgramNr ! = - 1 )
2013-03-31 14:09:38 +02:00
{
2013-04-10 00:35:25 +02:00
if ( ( channel . SignalSource & SignalSource . Analog ) ! = 0 )
2013-04-03 12:47:24 +02:00
+ + newAnalogChannelCount ;
2013-04-10 00:35:25 +02:00
else if ( ( channel . SignalSource & SignalSource . DvbCT ) ! = 0 )
2013-04-03 12:47:24 +02:00
+ + newDvbctChannelCount ;
2013-07-19 17:27:02 +02:00
}
2013-04-03 12:47:24 +02:00
channel . UpdateRawData ( ) ;
2013-03-31 14:09:38 +02:00
}
}
}
2013-07-19 17:27:02 +02:00
2013-03-31 14:09:38 +02:00
#endregion
2013-04-03 12:47:24 +02:00
#region ReorderActChannelsPhysically ( )
private void ReorderActChannelsPhysically ( )
2013-03-31 14:09:38 +02:00
{
2021-09-05 04:36:56 +02:00
var avbt = this . avbtTvChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var avbc = this . avbcTvChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var avb = avbt . Union ( avbc ) . ToList ( ) ;
this . ReorderChannelData ( this . analogBlockOffset + 8 , this . actChannelSize , this . analogChannelCount , avb ) ;
2013-04-03 23:26:09 +02:00
2013-06-23 00:11:16 +02:00
var dvbcTv = this . dvbcTvChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var dvbcRadio = this . dvbcRadioChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var dvbtTv = this . dvbtTvChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var dvbtRadio = this . dvbtRadioChannels . Channels . OrderBy ( c = > c . NewProgramNr ) ;
var dvb = dvbtTv . Union ( dvbtRadio ) . Union ( dvbcTv ) . Union ( dvbcRadio ) . ToList ( ) ;
this . ReorderChannelData ( this . dvbctBlockOffset + 8 , this . actChannelSize , this . dvbctChannelCount , dvb ) ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
#region ReorderChannelData ( )
private void ReorderChannelData ( int channelDataOffset , int channelDataLength , int recordCount , IList < ChannelInfo > sortedList )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
if ( sortedList . Count = = 0 ) return ;
byte [ ] copy = new byte [ recordCount * channelDataLength ] ;
Array . Copy ( fileContent , channelDataOffset , copy , 0 , copy . Length ) ;
int pTarget = channelDataOffset ;
int slot = 0 ;
foreach ( ChannelInfo appChannel in sortedList )
2013-03-31 14:09:38 +02:00
{
2013-11-09 16:30:59 +01:00
if ( appChannel . RecordIndex < 0 | | appChannel . NewProgramNr < = 0 & & removeDeletedActChannels )
2013-04-03 12:47:24 +02:00
continue ;
if ( appChannel . RecordIndex ! = slot )
{
Array . Copy ( copy , appChannel . RecordIndex * channelDataLength , fileContent , pTarget , channelDataLength ) ;
appChannel . RecordIndex = slot ;
}
+ + slot ;
pTarget + = channelDataLength ;
2013-03-31 14:09:38 +02:00
}
}
#endregion
2013-04-03 12:47:24 +02:00
#region UpdateDvbsChecksums ( )
private void UpdateDvbsChecksums ( )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
for ( int i = 0 ; i < this . dvbsSubblockCrcOffset . Length ; i + + )
2013-03-31 14:09:38 +02:00
{
2019-07-20 02:08:51 +02:00
uint crc32 = Crc32 . Reversed . CalcCrc32 ( fileContent , this . dvbsSubblockCrcOffset [ i ] + 4 , satConfig . dvbsSubblockLength [ i ] ) ;
2013-04-03 12:47:24 +02:00
var bytes = BitConverter . GetBytes ( crc32 ) ;
for ( int j = 0 ; j < bytes . Length ; j + + )
fileContent [ this . dvbsSubblockCrcOffset [ i ] + j ] = bytes [ j ] ;
2013-03-31 14:09:38 +02:00
}
}
#endregion
#region DefaultEncoding
public override Encoding DefaultEncoding
{
get { return base . DefaultEncoding ; }
set
{
if ( Equals ( value , this . DefaultEncoding ) )
return ;
base . DefaultEncoding = value ;
2013-09-15 18:27:04 +02:00
this . actMappings . DefaultEncoding = value ;
this . firmwareMappings . DefaultEncoding = value ;
this . dvbsMappings . DefaultEncoding = value ;
this . dvbsTransponderMappings . DefaultEncoding = value ;
this . lnbMappings . DefaultEncoding = value ;
2013-03-31 14:09:38 +02:00
if ( this . DataRoot . IsEmpty )
return ;
ChangeEncoding ( ) ;
}
}
#endregion
#region ChangeEncoding ( )
2013-04-03 12:47:24 +02:00
private void ChangeEncoding ( )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
foreach ( var list in DataRoot . ChannelLists )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
foreach ( var channel in list . Channels )
channel . ChangeEncoding ( this . DefaultEncoding ) ;
2013-03-31 14:09:38 +02:00
}
}
#endregion
2013-04-03 12:47:24 +02:00
// TvSettingsForm
2013-03-31 14:09:38 +02:00
#region GetFileInformation ( )
2013-04-03 12:47:24 +02:00
public override string GetFileInformation ( )
2013-03-31 14:09:38 +02:00
{
StringBuilder sb = new StringBuilder ( ) ;
2013-11-12 18:25:15 +01:00
sb . AppendLine ( base . GetFileInformation ( ) ) ;
2013-04-03 12:47:24 +02:00
sb . AppendLine ( "ANALOG" ) ;
sb . Append ( "Number of data records: " ) . Append ( this . analogChannelCount ) . AppendLine ( ) ;
sb . Append ( "Length of data record: " ) . Append ( this . actChannelSize ) . AppendLine ( ) ;
sb . AppendLine ( ) ;
sb . AppendLine ( ) ;
sb . AppendLine ( "DVB-C/T" ) ;
sb . Append ( "Number of data records: " ) . Append ( this . dvbctChannelCount ) . AppendLine ( ) ;
sb . Append ( "Length of data record: " ) . Append ( this . actChannelSize ) . AppendLine ( ) ;
sb . AppendLine ( ) ;
sb . AppendLine ( ) ;
sb . AppendLine ( "DVB-S" ) ;
if ( satConfig ! = null )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
int numberOfDupePrNr ;
CountDuplicateRecords ( out numberOfDupePrNr ) ;
sb . Append ( "Max number of data records: " ) . Append ( satConfig . dvbsMaxChannelCount ) . AppendLine ( ) ;
sb . Append ( "Length of data record: " ) . Append ( satConfig . dvbsChannelLength ) . AppendLine ( ) ;
sb . Append ( "Channel records in use: " ) . Append ( dvbsChannelCount ) . AppendLine ( ) ;
sb . Append ( "Channel records marked hard-deleted: " ) . Append ( this . deletedChannelsHard ) . AppendLine ( ) ;
sb . Append ( "Channel records marked soft-deleted: " ) . Append ( this . deletedChannelsSoft ) . AppendLine ( ) ;
sb . Append ( "Channel records erased (duplicates): " ) . Append ( this . duplicateChannels ) . AppendLine ( ) ;
sb . Append ( "Channel records with Pr# 0: " ) . Append ( dvbsChannelsAtPr0 ) . AppendLine ( ) ;
sb . Append ( "Channel records with duplicate Pr#: " ) . Append ( numberOfDupePrNr ) . AppendLine ( ) ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
else
sb . AppendLine ( "not present" ) ;
2013-03-31 14:09:38 +02:00
return sb . ToString ( ) ;
}
private void CountDuplicateRecords ( out int numberOfDupePrNr )
{
numberOfDupePrNr = 0 ;
foreach ( var list in this . DataRoot . ChannelLists )
{
if ( ( list . SignalSource & SignalSource . Sat ) ! = 0 )
numberOfDupePrNr + = list . DuplicateProgNrCount ;
}
}
#endregion
#region TvCountryCode
2013-04-03 12:47:24 +02:00
public string TvCountryCode
2013-03-31 14:09:38 +02:00
{
get { return this . countryCode ; }
set
{
2013-04-03 12:47:24 +02:00
if ( value . Length < 3 | | this . settingsBlockOffset = = 0 | | this . settingsBlockSize < 8 ) return ;
2021-08-31 22:13:28 +02:00
value = value . ToUpperInvariant ( ) ;
2013-04-03 12:47:24 +02:00
int off = this . settingsBlockOffset + 4 + 4 + 2 ;
for ( int i = 0 ; i < 3 ; i + + )
this . fileContent [ off - - ] = ( byte ) value [ i ] ;
2013-03-31 14:09:38 +02:00
this . countryCode = value ;
}
}
#endregion
#region ShowDeviceSettingsForm ( )
public override void ShowDeviceSettingsForm ( object parentWindow )
{
2021-07-24 18:59:03 +02:00
uiFactory . ShowTvSettingsForm ( this , parentWindow ) ;
2013-03-31 14:09:38 +02:00
}
#endregion
2013-04-03 12:47:24 +02:00
#region GetFirmwareMapping ( )
public FirmwareData GetFirmwareMapping ( )
{
var mapping = this . firmwareMappings . GetMapping ( this . firmwareBlockSize , false ) ;
if ( mapping = = null ) return null ;
mapping . SetDataPtr ( this . fileContent , this . firmwareBlockOffset ) ;
return mapping ;
}
#endregion
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
// Testing
2013-03-31 14:09:38 +02:00
2013-04-03 12:47:24 +02:00
#region GetHotelMenuOffset ( )
public int GetHotelMenuOffset ( )
2013-03-31 14:09:38 +02:00
{
2013-04-03 12:47:24 +02:00
int off = this . firmwareBlockOffset ;
for ( int i = 6500 ; i < this . FirmwareBlockSize - 3 ; i + + )
{
if ( BitConverter . ToUInt32 ( this . fileContent , off + i ) = = 0x05000101 ) // 1,1,0,5
{
for ( int j = 5 ; j < 20 ; j + + ) // backtrack to find Volume/MaxVolue pattern
{
if ( fileContent [ off + i - j ] = = 101 & & fileContent [ off + i - j - 6 ] = = 100 )
// check for Volume/MaxVolue to be 101/100
2013-04-03 13:30:08 +02:00
return i - j - 15 ;
2013-04-03 12:47:24 +02:00
}
2013-04-03 13:30:08 +02:00
return 0 ;
2013-04-03 12:47:24 +02:00
}
}
2013-04-03 13:30:08 +02:00
return 0 ;
2013-03-31 14:09:38 +02:00
}
2013-04-03 12:47:24 +02:00
#endregion
2013-03-31 14:09:38 +02:00
2013-05-05 22:40:57 +02:00
internal bool IsTesting { get ; set ; }
2013-04-03 12:47:24 +02:00
internal int ACTChannelLength { get { return this . actChannelSize ; } }
2013-05-30 09:28:01 +02:00
internal bool HasDvbs { get { return satConfig ! = null ; } }
2013-04-03 12:47:24 +02:00
internal int SatChannelLength { get { return satConfig ! = null ? satConfig . dvbsChannelLength : 0 ; } }
2013-05-11 16:45:29 +02:00
internal decimal DvbsSymbolRateCorrectionFactor { get { return this . dvbsSymbolRateFactor ; } }
2013-04-03 12:47:24 +02:00
internal int FirmwareBlockSize { get { return this . firmwareBlockSize ; } }
2013-03-31 14:09:38 +02:00
}
}