- Philips: fixes for ChannelMap_100, 105 and 110 formats

- Philips: support for FLASH/*.bin DVB-T/C and preset DVB-S lists (mgr_chan_s_pkg.db)
- Toshiba: lists with chmgt_type001\\chmgt.bin can now be opened without zipping them
- Toshiba: selecting the hotelopt_type001.bin will now also load the list (if the type is supported)
- Alden: added support for "Alden" Android SmartTV channel list format (dvr_rtk_tv.db)
This commit is contained in:
Horst Beham
2021-09-06 20:42:45 +02:00
parent 79fb621d69
commit 9c2a7b9195
20 changed files with 796 additions and 98 deletions

View File

@@ -9,6 +9,9 @@ namespace ChanSort.Api
{
public abstract class SerializerBase : IDisposable
{
public const string ERR_UnknownFormat = "unknown channel list format";
public const string ERR_UnsupportedFormat = "Detected a known but unsupported channel list format: {0}";
#region class SupportedFeatures
public enum DeleteMode

View File

@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Data.Sqlite;
using ChanSort.Api;
namespace ChanSort.Loader.Android
{
/*
Android TVs typically store a SQLite based data file containing an "android_metadata" table. The rest differs from brand to brand.
Philips ChannelMap_30 and _45 use the android database format for the tv.db file. That's handled in the Philips loaders, not here.
Alden uses a variant very similar to Philips, but instead of a single "channels" table containing channels from all sources,
it has distinct (atv|dtv)_(cable|antena|satellite)_channels tables. And TV and Radio channels are also put in different lists, starting at 1 each
*/
public class AldenSerializer : SerializerBase
{
private readonly ChannelList analChannels = new ChannelList(SignalSource.Analog, "Analog");
private readonly ChannelList dvbtTvChannels = new ChannelList(SignalSource.DvbT|SignalSource.Tv, "DVB-T TV");
private readonly ChannelList dvbtRadioChannels = new ChannelList(SignalSource.DvbT|SignalSource.Radio, "DVB-T Radio");
private readonly ChannelList dvbtDataChannels = new ChannelList(SignalSource.DvbT | SignalSource.Data, "DVB-T Data");
private readonly ChannelList dvbcTvChannels = new ChannelList(SignalSource.DvbC|SignalSource.Tv, "DVB-C TV");
private readonly ChannelList dvbcRadioChannels = new ChannelList(SignalSource.DvbC|SignalSource.Radio, "DVB-C Radio");
private readonly ChannelList dvbcDataChannels = new ChannelList(SignalSource.DvbC | SignalSource.Data, "DVB-C Data");
private readonly ChannelList dvbsTvChannels = new ChannelList(SignalSource.DvbS|SignalSource.Tv, "DVB-S TV");
private readonly ChannelList dvbsRadioChannels = new ChannelList(SignalSource.DvbS|SignalSource.Radio, "DVB-S Radio");
private readonly ChannelList dvbsDataChannels = new ChannelList(SignalSource.DvbS | SignalSource.Data, "DVB-S Data");
private readonly StringBuilder logMessages = new StringBuilder();
private readonly Tuple<string, ChannelList, ChannelList, ChannelList>[] subLists;
#region ctor()
public AldenSerializer(string inputFile) : base(inputFile)
{
this.Features.ChannelNameEdit = ChannelNameEditMode.None;
this.Features.CanSkipChannels = true;
this.Features.CanLockChannels = true;
this.Features.CanHideChannels = true;
this.Features.DeleteMode = DeleteMode.Physically;
this.Features.CanSaveAs = true;
this.Features.CanHaveGaps = true; // at least the DVB-S Data list had gaps
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
this.Features.MaxFavoriteLists = 1;
this.Features.AllowGapsInFavNumbers = false;
this.Features.CanEditFavListNames = false;
this.subLists = new[]
{
Tuple.Create("atv_cable_channels", analChannels, (ChannelList)null, (ChannelList)null),
Tuple.Create("dtv_antena_channels", dvbtTvChannels, dvbtRadioChannels, dvbtDataChannels),
Tuple.Create("dtv_cable_channels", dvbcTvChannels, dvbcRadioChannels, dvbcDataChannels),
Tuple.Create("dtv_satellite_channels", dvbsTvChannels, dvbsRadioChannels, dvbsDataChannels)
};
foreach (var subList in subLists)
{
this.DataRoot.AddChannelList(subList.Item2);
if (subList.Item3 != null)
this.DataRoot.AddChannelList(subList.Item3);
if (subList.Item4 != null)
this.DataRoot.AddChannelList(subList.Item4);
}
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ShortName));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ServiceType));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ServiceTypeName));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Satellite));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Provider));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.PcrPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.VideoPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.AudioPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ChannelOrTransponder));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Provider));
}
}
#endregion
// loading
#region Load()
public override void Load()
{
using var conn = new SqliteConnection($"Data Source={this.FileName}");
conn.Open();
using var cmd = conn.CreateCommand();
foreach (var table in new[] { "dtv_satellite_channels" })
{
cmd.CommandText = $"select count(1) from sqlite_master where type='table' and name='{table}'";
if ((long)cmd.ExecuteScalar() == 0)
throw new FileLoadException(ERR_UnknownFormat);
}
var columns = "_id, type, service_type, original_network_id, transport_stream_id, service_id, display_number, display_name, browsable, searchable, locked, "
+ "internal_provider_flag1, internal_provider_flag4, favorite, scrambled, channel_index";
var fields = columns.Split(',');
var c = new Dictionary<string, int>();
for (int i = 0; i < fields.Length; i++)
c[fields[i].Trim()] = i;
foreach (var subList in this.subLists)
{
cmd.CommandText = $"select count(1) from sqlite_master where type='table' and name='{subList.Item1}'";
if ((long)cmd.ExecuteScalar() == 0)
continue;
cmd.CommandText = $"select {columns} from {subList.Item1}";
using var r = cmd.ExecuteReader();
while (r.Read())
{
var ch = new ChannelInfo(SignalSource.DvbS, r.GetInt32(c["_id"]), r.GetInt32(c["display_number"]), r.GetString(c["display_name"]));
ch.OriginalNetworkId = r.GetInt16(c["original_network_id"]);
ch.TransportStreamId = r.GetInt16(c["transport_stream_id"]);
ch.ServiceId = r.GetInt16(c["service_id"]);
ch.Hidden = r.GetInt16(c["browsable"]) == 0;
ch.Skip = r.GetInt16(c["searchable"]) == 0;
ch.Lock = r.GetInt16(c["locked"]) != 0;
ch.FreqInMhz = (decimal)r.GetInt64(c["internal_provider_flag1"]) / 100000;
ch.SymbolRate = r.GetInt32(c["internal_provider_flag4"]) / 1000;
var f = r.GetInt32(c["favorite"]); // unknown if this is a flag or an ordered number for a single fav list. assuming the latter for now
ch.SetOldPosition(1, f == 0 ? -1 : f);
ch.Encrypted = r.GetBoolean(c["scrambled"]);
ch.RecordOrder = r.GetInt32(c["channel_index"]);
var source = r.GetString(c["service_type"]);
ChannelList list;
if (source == "SERVICE_TYPE_AUDIO_VIDEO")
{
list = subList.Item2;
ch.SignalSource |= SignalSource.Tv;
}
else if (source == "SERVICE_TYPE_AUDIO")
{
list = subList.Item3;
ch.SignalSource |= SignalSource.Radio;
}
else
{
list = subList.Item4;
ch.SignalSource |= SignalSource.Data;
}
this.DataRoot.AddChannel(list, ch);
}
}
}
#endregion
#region Save()
/// <summary>
/// The "tv.db" file was reported to exist as early as in ChannelMap_25 format and has been seen in formats 30 and 45 too
/// </summary>
public override void Save(string outputFile)
{
this.FileName = outputFile;
using var conn = new SqliteConnection($"Data Source={outputFile}");
conn.Open();
using var trans = conn.BeginTransaction();
using var cmd = conn.CreateCommand();
cmd.Parameters.Clear();
cmd.Parameters.Add("@id", SqliteType.Integer);
cmd.Parameters.Add("@prNum", SqliteType.Text);
cmd.Parameters.Add("@name", SqliteType.Text);
cmd.Parameters.Add("@browsable", SqliteType.Integer);
cmd.Parameters.Add("@searchable", SqliteType.Integer);
cmd.Parameters.Add("@locked", SqliteType.Integer);
using var del = conn.CreateCommand();
del.Parameters.Add("@id", SqliteType.Integer);
foreach (var list in this.DataRoot.ChannelLists)
{
var table = this.subLists.First(sl => sl.Item2 == list || sl.Item3 == list || sl.Item4 == list).Item1;
cmd.CommandText = $"update {table} set display_number=@prNum, display_name=@name, browsable=@browsable, searchable=@searchable, locked=@locked where _id=@id";
cmd.Prepare();
del.CommandText = $"delete from {table} where _id=@id";
del.Prepare();
foreach (var ch in list.Channels)
{
if (ch.IsProxy)
continue;
if (ch.IsDeleted)
{
del.Parameters["@id"].Value = ch.RecordOrder;
del.ExecuteNonQuery();
}
else
{
cmd.Parameters["@id"].Value = ch.RecordIndex;
cmd.Parameters["@prNum"].Value = ch.NewProgramNr.ToString();
cmd.Parameters["@name"].Value = ch.Name;
cmd.Parameters["@browsable"].Value = ch.Hidden ? 0 : 1; // TODO check which one is which (Skip/Hide)
cmd.Parameters["@searchable"].Value = ch.Skip ? 0 : 1;
cmd.Parameters["@locked"].Value = ch.Lock ? 1 : 0;
cmd.ExecuteNonQuery();
}
}
}
trans.Commit();
conn.Close();
}
#endregion
// framework support methods
#region GetFileInformation
public override string GetFileInformation()
{
return base.GetFileInformation() + this.logMessages.Replace("\n", "\r\n");
}
#endregion
}
}

View File

@@ -0,0 +1,23 @@
using System.IO;
using ChanSort.Api;
namespace ChanSort.Loader.Android
{
public class AndroidPlugin : ISerializerPlugin
{
public string DllName { get; set; }
public string PluginName => "Android (*.db)";
public string FileFilter => "*.db";
public SerializerBase CreateSerializer(string inputFile)
{
var file = Path.GetFileName(inputFile).ToLowerInvariant();
// dvr_rtk_tv.db known from "Alden"
if (file.StartsWith("dvr_rtk_tv") && file.EndsWith(".db"))
return new AldenSerializer(inputFile);
throw new FileLoadException(SerializerBase.ERR_UnknownFormat);
}
}
}

View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5088DB0D-6BDE-4678-8C50-A14E6A294A45}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ChanSort.Loader.Android</RootNamespace>
<AssemblyName>ChanSort.Loader.Android</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Data.Sqlite, Version=5.0.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Sqlite.Core.5.0.8\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_v2, Version=2.0.4.976, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.batteries_v2.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=2.0.4.976, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.core.2.0.4\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.nativelibrary, Version=2.0.4.976, Culture=neutral, PublicKeyToken=502ed628492ab262, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.nativelibrary.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.0.4.976, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.0.4\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AndroidPlugin.cs" />
<Compile Include="AldenSerializer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChanSort.Api\ChanSort.Api.csproj">
<Project>{dccffa08-472b-4d17-bb90-8f513fc01392}</Project>
<Name>ChanSort.Api</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ChanSort.Loader.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ChanSort.Loader.Android")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5088db0d-6bde-4678-8c50-a14e6a294a45")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Data.Sqlite" version="5.0.8" targetFramework="net48" />
<package id="Microsoft.Data.Sqlite.Core" version="5.0.8" targetFramework="net48" />
<package id="SQLitePCLRaw.bundle_e_sqlite3" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.core" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.lib.e_sqlite3" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.0.4" targetFramework="net48" />
<package id="System.Buffers" version="4.4.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
</packages>

View File

@@ -10,7 +10,6 @@ namespace ChanSort.Loader.Panasonic
{
class Serializer : SerializerBase
{
private const string ERR_FileFormatOrEncryption = "File uses an unknown format or encryption";
private readonly ChannelList avbtChannels = new ChannelList(SignalSource.AnalogT, "Analog Antenna");
private readonly ChannelList avbcChannels = new ChannelList(SignalSource.AnalogC, "Analog Cable");
private readonly ChannelList dvbtChannels = new ChannelList(SignalSource.DvbT, "DVB-T");
@@ -97,7 +96,7 @@ namespace ChanSort.Loader.Panasonic
{
this.cypherMode = this.GetCypherMode(this.FileName);
if (cypherMode == CypherMode.Unknown)
throw new FileLoadException(ERR_FileFormatOrEncryption);
throw new FileLoadException(ERR_UnknownFormat);
if (cypherMode == CypherMode.None)
return this.FileName;

View File

@@ -8,6 +8,8 @@ reorderRecordsByChannelNumber=false
############################################################################
# Repair\channel_db_ver.db, atv_chan_phy_c.db, FLASH_DTVINFO_S_FTA, mgr_chan_s_fta.db, ... - experimental
# unfortunately read-only because all attempts so far produced inconsistencies between the channels in the menu and what the tuner actually put on screen
# There is a program number at offset 0 and another one around 450. My guess is that the 1st is the "new" number and the 2nd the "old" number - or it is the other way around.
# When both numbers are updated and also the value of RecordIndex is set to PrNr-1, the TV doesn't show any changes.
[mgr_chan_s_fta.db]
lenHeader=64
@@ -15,23 +17,96 @@ lenEntry=476
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=false
allowEdit=true
[mgr_chan_s_fta.db_entry]
# there is a program number at offset 0 and 452. My guess is that 0 is the "new" number and 452 the "old" number, which should be left untouched. Or it is the other way around.
# when both numbers are updated and also the value at 456 set to PrNr-1, the TV didn't show any changes.
offProgNr=0
offName=20
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
[mgr_chan_s_pkg.db]
lenHeader=64
lenEntry=480
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=true
[mgr_chan_s_pkg.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
[mgr_chan_dvbt.db]
lenHeader=64
lenEntry=472
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=true
[mgr_chan_dvbt.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offProvider=224
lenProvider=200
offFreq=440
offOldProgNr=448
offRecordIndex=456
offTsid=460
offSymbolRate=462
offSid=464
offOnid=466
[mgr_chan_dvbc.db]
lenHeader=64
lenEntry=472
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=true
[mgr_chan_dvbc.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offProvider=224
lenProvider=200
offFreq=440
offOldProgNr=448
offRecordIndex=456
offTsid=460
offSymbolRate=462
offSid=464
offOnid=466
############################################################################
# Repair\ChannelList\chanLst.bin version 1.x with AntennaDigSrvTable, CableDigSrvTable and service.dat (for DVB-S) - should be stable

View File

@@ -11,17 +11,18 @@ namespace ChanSort.Loader.Philips
/*
* This serializer is used for the channel list format with a Repair\ folder containing files like channel_db_ver.db, mgr_chan_s_fta.db, ...
* The .db files are proprietary binary files, not SQLite databases.
* So far only the mgr_chan_s_fta.db file holing DVB-S channels is reverse engineered, the offsets are defined in PChanSort.Loader.Philips.ini
*
* Unfortunately modifying the .db files does not seem to be enough. The TV also depends on channel data in the FLASH_* files, which I don't know how how to edit.
* Therefore lists of this format can be read as read-only reference lists, but modifications are disabled.
* Due to lack of sample lists, the analog and DVB-C files have not been reverse engineered yet.
* The data offsets are defined in ChanSort.Loader.Philips.ini
*/
class DbSerializer : SerializerBase
{
private readonly IniFile ini;
private readonly List<string> dataFilePaths = new List<string>();
private readonly ChannelList dvbsChannels = new ChannelList(SignalSource.DvbS, "DVB-S");
private readonly ChannelList dvbtChannels = new ChannelList(SignalSource.DvbT, "DVB-T");
private readonly ChannelList dvbcChannels = new ChannelList(SignalSource.DvbT, "DVB-C");
private readonly ChannelList dvbsFtaChannels = new ChannelList(SignalSource.DvbS | SignalSource.Provider0, "DVB-S FTA");
private readonly ChannelList dvbsPkgChannels = new ChannelList(SignalSource.DvbS | SignalSource.Provider1, "DVB-S Preset");
private readonly Dictionary<ChannelList, string> fileByList = new();
public DbSerializer(string inputFile) : base(inputFile)
@@ -29,27 +30,30 @@ namespace ChanSort.Loader.Philips
this.Features.MaxFavoriteLists = 1;
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
this.Features.DeleteMode = DeleteMode.NotSupported;
this.Features.CanHaveGaps = false;
this.Features.CanHaveGaps = true; // the mgr_chan_s_pkg can have gaps
string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini");
this.ini = new IniFile(iniFile);
this.DataRoot.AddChannelList(dvbsChannels);
dvbsChannels.VisibleColumnFieldNames = new List<string>
this.DataRoot.AddChannelList(dvbtChannels);
this.DataRoot.AddChannelList(dvbcChannels);
this.DataRoot.AddChannelList(dvbsFtaChannels);
this.DataRoot.AddChannelList(dvbsPkgChannels);
foreach (var list in this.DataRoot.ChannelLists)
{
"Position", //nameof(Channel.NewProgramNr),
"OldPosition", // nameof(Channel.OldProgramNr),
nameof(Channel.Name),
nameof(Channel.Favorites),
nameof(Channel.FreqInMhz),
nameof(Channel.SymbolRate),
nameof(Channel.TransportStreamId),
nameof(Channel.OriginalNetworkId),
nameof(Channel.ServiceId)
};
var sec = ini.GetSection("mgr_chan_s_fta.db");
dvbsChannels.ReadOnly = sec.GetBool("allowEdit", false);
list.VisibleColumnFieldNames = new List<string>
{
"Position", //nameof(Channel.NewProgramNr),
"OldPosition", // nameof(Channel.OldProgramNr),
nameof(Channel.Name),
nameof(Channel.Favorites),
nameof(Channel.FreqInMhz),
nameof(Channel.SymbolRate),
nameof(Channel.TransportStreamId),
nameof(Channel.OriginalNetworkId),
nameof(Channel.ServiceId)
};
}
}
#region Load()
@@ -57,23 +61,42 @@ namespace ChanSort.Loader.Philips
{
bool validList = false;
foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName)))
foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName) ?? ""))
{
var lc = Path.GetFileName(file).ToLowerInvariant();
switch (lc)
{
case "atv_channel_t.db":
// TODO: no sample file yet that contains analog terrestrial channels
break;
case "atv_channel_c.db":
// TODO: no sample file yet that contains analog cable channels
break;
case "channel_db_ver.db":
LoadVersion(file);
break;
case "mgr_chan_dvbt.db":
LoadDvb(file, lc, dvbtChannels);
validList = true;
break;
case "mgr_chan_dvbc.db":
// no sample file with DVB-C data yet, so this here is a guess based on DVB-T
LoadDvb(file, lc, dvbcChannels);
validList = true;
break;
case "mgr_chan_s_fta.db":
LoadDvbs(file);
LoadDvb(file, lc, dvbsFtaChannels);
validList = true;
break;
case "mgr_chan_s_pkg.db":
LoadDvb(file, lc, dvbsPkgChannels);
validList = true;
break;
}
}
if (!validList)
throw new FileLoadException(this.FileName + " is not a supported Philips Repair/mgr_chan_s_fta.db channel list");
throw new FileLoadException(this.FileName + " is not a supported Philips Repair/channel_db_ver.db channel list");
}
#endregion
@@ -96,7 +119,7 @@ namespace ChanSort.Loader.Philips
this.FileFormatVersion += $":{data[6]:D2}";
// Philips doesn't export any information about the TV model in this format. For automated stats I manually place modelinfo.txt files in the folders
for (var dir = Path.GetDirectoryName(file); dir != ""; dir = Path.GetDirectoryName(dir))
for (var dir = Path.GetDirectoryName(file); dir != null; dir = Path.GetDirectoryName(dir))
{
var path = Path.Combine(dir, "modelinfo.txt");
if (File.Exists(path))
@@ -109,11 +132,12 @@ namespace ChanSort.Loader.Philips
#endregion
#region LoadDvbs()
private void LoadDvbs(string file)
private void LoadDvb(string path, string sectionName, ChannelList list)
{
var data = File.ReadAllBytes(file);
var signalSource = list.SignalSource;
var data = File.ReadAllBytes(path);
var sec = ini.GetSection("mgr_chan_s_fta.db");
var sec = ini.GetSection(sectionName);
var lenHeader = sec.GetInt("lenHeader");
var lenFooter = sec.GetInt("lenFooter");
var lenEntry = sec.GetInt("lenEntry");
@@ -121,9 +145,11 @@ namespace ChanSort.Loader.Philips
var records = (data.Length - lenHeader - lenFooter) / lenEntry;
if (records <= 0)
throw new FileLoadException("Currently only DVB-S lists are supported and mgr_chan_s_fta.db contains no channels.");
return;
var mapping = new DataMapping(this.ini.GetSection("mgr_chan_s_fta.db_entry"));
list.ReadOnly = !sec.GetBool("allowEdit", false);
var mapping = new DataMapping(this.ini.GetSection(sectionName + "_entry"));
sec = ini.GetSection("mgr_chan_s_fta.db_entry");
var lenName = sec.GetInt("lenName");
for (int i = 0; i < records; i++)
@@ -136,26 +162,30 @@ namespace ChanSort.Loader.Philips
var name = data[off + 0] == 0 ? (data[off + 1] == 0 ? "" : Encoding.BigEndianUnicode.GetString(data, off, lenName)) : DefaultEncoding.GetString(data, off, lenName);
name = name.TrimEnd('\0');
var ch = new Channel(SignalSource.DvbS, i, oldProgNr, name);
var ch = new Channel(signalSource, i, oldProgNr, name);
ch.RecordOrder = i;
var favPos = mapping.GetWord("offFav");
if (favPos > 0)
ch.SetOldPosition(1, favPos);
ch.SymbolRate = mapping.GetWord("offSymbolRate");
ch.FreqInMhz = mapping.GetWord("offFreq");
ch.FreqInMhz = mapping.GetDword("offFreq");
if (ch.FreqInMhz > 13000) // DVB-S stores value in MHz, DVB-T in Hz
ch.FreqInMhz /= 1000;
if (ch.FreqInMhz > 13000)
ch.FreqInMhz /= 1000;
ch.TransportStreamId = mapping.GetWord("offTsid");
ch.OriginalNetworkId = mapping.GetWord("offOnid");
ch.ServiceId = mapping.GetWord("offSid");
this.DataRoot.AddChannel(dvbsChannels, ch);
this.DataRoot.AddChannel(list, ch);
}
var offChecksum = data.Length - lenFooter + offFooterChecksum;
var expectedChecksum = BitConverter.ToUInt16(data, offChecksum);
var actualChecksum = CalcChecksum(data, 0, offChecksum);
if (actualChecksum != expectedChecksum)
throw new FileLoadException($"File {file} contains invalid checksum. Expected {expectedChecksum:x4} but calculated {actualChecksum:x4}");
throw new FileLoadException($"File {path} contains invalid checksum. Expected {expectedChecksum:x4} but calculated {actualChecksum:x4}");
this.dataFilePaths.Add(file);
this.fileByList[list] = path;
}
#endregion
@@ -177,46 +207,43 @@ namespace ChanSort.Loader.Philips
}
#endregion
public override IEnumerable<string> GetDataFilePaths() => this.dataFilePaths.ToList();
public override IEnumerable<string> GetDataFilePaths() => this.fileByList.Values.ToList();
#region Save()
public override void Save(string tvOutputFile)
{
foreach (var file in this.dataFilePaths)
foreach (var listAndFile in this.fileByList)
{
var lc = Path.GetFileName(file).ToLowerInvariant();
switch (lc)
{
case "mgr_chan_s_fta.db":
SaveDvbs(file);
break;
}
var list = listAndFile.Key;
var file = listAndFile.Value;
var secName = Path.GetFileName(file).ToLowerInvariant();
SaveDvb(file, secName, list);
}
}
private void SaveDvbs(string file)
private void SaveDvb(string file, string secName, ChannelList list)
{
var data = File.ReadAllBytes(file);
var sec = ini.GetSection("mgr_chan_s_fta.db");
var sec = ini.GetSection(secName);
var lenHeader = sec.GetInt("lenHeader");
var lenFooter = sec.GetInt("lenFooter");
var lenEntry = sec.GetInt("lenEntry");
var offFooterChecksum = sec.GetInt("offFooterChecksum");
var mapping = new DataMapping(ini.GetSection("mgr_chan_s_fta.db_entry"));
var mapping = new DataMapping(ini.GetSection(secName + "_entry"));
if (sec.GetBool("reorderRecordsByChannelNumber"))
{
// physically reorder channels
var newData = new byte[data.Length];
Array.Copy(data, newData, lenHeader);
var off = lenHeader + lenEntry * dvbsChannels.Channels.Count;
var off = lenHeader + lenEntry * list.Channels.Count;
Array.Copy(data, off, newData, off, lenFooter);
int i = 0;
foreach (var ch in dvbsChannels.Channels.OrderBy(c => c.NewProgramNr))
foreach (var ch in list.Channels.OrderBy(c => c.NewProgramNr))
{
off = lenHeader + i * lenEntry;
Array.Copy(data, lenHeader + ch.RecordOrder * lenEntry, newData, off, lenEntry);
@@ -233,7 +260,7 @@ namespace ChanSort.Loader.Philips
else
{
// update channel data
foreach (var ch in dvbsChannels.Channels)
foreach (var ch in list.Channels)
{
mapping.SetDataPtr(data, lenHeader + ch.RecordOrder * lenEntry);
mapping.SetWord("offProgNr", ch.NewProgramNr);

View File

@@ -136,7 +136,7 @@ namespace ChanSort.Loader.Philips
if (majorVersion == -1)
return new DbSerializer(inputFile);
throw new FileLoadException($"Philips ChannelMap format version {majorVersion} is not supported.");
throw new FileLoadException(majorVersion == int.MinValue ? SerializerBase.ERR_UnknownFormat : $"Philips ChannelMap format version {majorVersion} is not supported (yet).");
}
}
}

View File

@@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Schema;
using ChanSort.Api;
@@ -378,9 +377,15 @@ namespace ChanSort.Loader.Philips
// use special configs for version 100 variants
var dir = Path.GetDirectoryName(this.FileName) ?? "";
if (File.Exists(Path.Combine(dir, "atv_cmdb.bin")))
{
this.iniMapSection = ini.GetSection("Map100_cmdb.bin");
this.FileFormatVersion += "/cmdb";
}
else if (File.Exists(Path.Combine(dir, "channelFile.bin")))
{
this.iniMapSection = ini.GetSection("Map100_channelFile.bin");
this.FileFormatVersion += "/channelFile";
}
if (this.iniMapSection?.GetBool("setReorderedFavNumber") ?? false)
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
@@ -762,7 +767,7 @@ namespace ChanSort.Loader.Philips
#region ReorderNodes()
private void ReorderNodes(FileData file)
{
var progNrAttrib = file.formatVersion == 1 ? "presetnumber" : "ChannelNumer";
var progNrAttrib = file.formatVersion == 1 ? "presetnumber" : "ChannelNumber";
var nodes = file.doc.DocumentElement.GetElementsByTagName("Channel");
var list = new List<XmlElement>();

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using ChanSort.Api;
using Microsoft.Data.Sqlite;
@@ -17,6 +18,8 @@ namespace ChanSort.Loader.Toshiba
private readonly ChannelList satRadioChannels = new(SignalSource.DvbS | SignalSource.Radio, "Sat-Radio");
private readonly ChannelList satTvChannels = new(SignalSource.DvbS | SignalSource.Tv, "Sat-TV");
private string workingDir;
#region ctor()
public ChmgtDbSerializer(string inputFile) : base(inputFile)
@@ -41,9 +44,16 @@ namespace ChanSort.Loader.Toshiba
public override void Load()
{
UnzipFileToTempFolder();
// this.FileName can be either hotelopt_type001.bin (as an anchor for the directory structure), or a .zip file containing that directory structure
if (Path.GetExtension(this.FileName).ToLowerInvariant() == ".zip")
{
UnzipFileToTempFolder();
workingDir = this.TempPath;
}
else
workingDir = Path.GetDirectoryName(this.FileName);
var sysDataConnString = "Data Source=" + TempPath + FILE_dvbSysData_db;
var sysDataConnString = "Data Source=" + this.workingDir + FILE_dvbSysData_db;
using (var conn = new SqliteConnection(sysDataConnString))
{
conn.Open();
@@ -53,7 +63,7 @@ namespace ChanSort.Loader.Toshiba
ReadTransponders(cmd);
}
var mainDataConnString = "Data Source=" + TempPath + FILE_dvbMainData_db;
var mainDataConnString = "Data Source=" + this.workingDir + FILE_dvbMainData_db;
using (var conn = new SqliteConnection(mainDataConnString))
{
conn.Open();
@@ -61,7 +71,7 @@ namespace ChanSort.Loader.Toshiba
ReadCryptInfo(cmd);
}
var channelConnString = "Data Source=" + TempPath + FILE_chmgt_db;
var channelConnString = "Data Source=" + this.workingDir + FILE_chmgt_db;
using (var conn = new SqliteConnection(channelConnString))
{
conn.Open();
@@ -250,7 +260,7 @@ namespace ChanSort.Loader.Toshiba
public override void Save(string tvOutputFile)
{
var channelConnString = "Data Source=" + TempPath + FILE_chmgt_db;
var channelConnString = "Data Source=" + this.workingDir + FILE_chmgt_db;
using (var conn = new SqliteConnection(channelConnString))
{
conn.Open();
@@ -268,7 +278,8 @@ namespace ChanSort.Loader.Toshiba
RepairCorruptedDatabaseImage(cmd);
}
ZipToOutputFile(tvOutputFile);
if (Path.GetExtension(this.FileName).ToLowerInvariant() == ".zip")
ZipToOutputFile(tvOutputFile);
}
#endregion

View File

@@ -6,15 +6,63 @@ namespace ChanSort.Loader.Toshiba
public class ToshibaPlugin : ISerializerPlugin
{
public string DllName { get; set; }
public string PluginName => "Toshiba (*.zip, settingsDB.db)";
public string FileFilter => "*.zip;*.db";
public string PluginName => "Toshiba";
public string FileFilter => "*.db;*.bin;*.zip";
public SerializerBase CreateSerializer(string inputFile)
{
if (Path.GetExtension(inputFile).ToLowerInvariant() == ".db")
var dir = Path.GetDirectoryName(inputFile);
var name = Path.GetFileName(inputFile).ToLowerInvariant();
var ext = Path.GetExtension(name);
const string FILE_hotelopt = "hotelopt_type001.bin";
// CLONE00001\settingsDB.db
if (name == "settingsdb.db")
return new SettingsDbSerializer(inputFile);
else
// selecting a chmgt.db, dvbMainData.db or dvbSysData.db directly -> use hotelopt_type001.bin instead
if (ext == ".db")
{
var hotelopt = Path.Combine(Path.GetDirectoryName(dir), FILE_hotelopt);
if (File.Exists(hotelopt))
return new ChmgtDbSerializer(hotelopt);
}
// hotelopt_type001.bin can belong to different formats
if (name == FILE_hotelopt)
{
if (File.Exists(Path.Combine(dir, "chmgt_type001", "chmgt.db")))
return new ChmgtDbSerializer(inputFile);
// atv_cmdb.bin is handledby the CmdbBin loader
if (File.Exists(Path.Combine(dir, "atv_cmdb.bin")))
return null;
// "Acropolis" format with chmgt_type001\*.txt is not supported
throw new FileLoadException(string.Format(SerializerBase.ERR_UnsupportedFormat, "Euro*.txt"));
}
// chmgt.db folder structure in a .zip file (for backward-compatibility with older ChanSort versions)
if (ext == ".zip")
return new ChmgtDbSerializer(inputFile);
// *.sdx is handled by the SatcoDX loader
if (ext == ".sdx")
return null;
// atv_cmdb.bin is handled by the CmdbBin loader
if ((name.StartsWith("atv_") || name.StartsWith("dtv_")) && ext == ".bin")
return null;
// Hotel\tcl_clone_user.bin
if (name.StartsWith("tcl_clone_") && name.EndsWith(".bin"))
throw new FileLoadException(string.Format(SerializerBase.ERR_UnsupportedFormat, "tcl_clone_user.bin"));
// HOTEL_*.bin
if (name.StartsWith("hotel_") && name.EndsWith(".bin"))
throw new FileLoadException(string.Format(SerializerBase.ERR_UnsupportedFormat, "HOTEL_*.bin"));
throw new FileLoadException(SerializerBase.ERR_UnknownFormat);
}
}
}

View File

@@ -90,6 +90,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.LG.UI", "Ch
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.CmdbBin", "ChanSort.Loader.CmdbBin\ChanSort.Loader.CmdbBin.csproj", "{B594DDA4-7BD5-450E-B648-668E0F659813}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Android", "ChanSort.Loader.Android\ChanSort.Loader.Android.csproj", "{5088DB0D-6BDE-4678-8C50-A14E6A294A45}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
All_Debug|Any CPU = All_Debug|Any CPU
@@ -1097,6 +1099,36 @@ Global
{B594DDA4-7BD5-450E-B648-668E0F659813}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B594DDA4-7BD5-450E-B648-668E0F659813}.Release|x86.ActiveCfg = Release|Any CPU
{B594DDA4-7BD5-450E-B648-668E0F659813}.Release|x86.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|Any CPU.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|x86.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Debug|x86.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|Any CPU.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|Any CPU.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|Mixed Platforms.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|x86.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.All_Release|x86.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|x86.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Debug|x86.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|Any CPU.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|x86.ActiveCfg = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.NoDevExpress_Debug|x86.Build.0 = Debug|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|Any CPU.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|x86.ActiveCfg = Release|Any CPU
{5088DB0D-6BDE-4678-8C50-A14E6A294A45}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -522,6 +522,14 @@
<Project>{DCCFFA08-472B-4D17-BB90-8F513FC01392}</Project>
<Name>ChanSort.Api</Name>
</ProjectReference>
<ProjectReference Include="..\ChanSort.Loader.Android\ChanSort.Loader.Android.csproj">
<Project>{5088db0d-6bde-4678-8c50-a14e6a294a45}</Project>
<Name>ChanSort.Loader.Android</Name>
</ProjectReference>
<ProjectReference Include="..\ChanSort.Loader.CmdbBin\ChanSort.Loader.CmdbBin.csproj">
<Project>{b594dda4-7bd5-450e-b648-668e0f659813}</Project>
<Name>ChanSort.Loader.CmdbBin</Name>
</ProjectReference>
<ProjectReference Include="..\ChanSort.Loader.Enigma2\ChanSort.Loader.Enigma2.csproj">
<Project>{4ad7f77e-617c-4741-82ae-e7a41c85ee4d}</Project>
<Name>ChanSort.Loader.Enigma2</Name>

View File

@@ -567,7 +567,7 @@ namespace ChanSort.Ui
{
serializer?.Dispose();
var errMsg = ex is FileLoadException ? ex.Message : ex.ToString(); // FileLoadExceptions are normal when a Loader does not support a file. No stack trace needed
errorMsgs.AppendLine($"{plugin.DllName} ({plugin.PluginName}): {errMsg}\n\n");
errorMsgs.AppendLine($"{plugin.PluginName}: {errMsg}\n\n");
if (ex is ArgumentException)
{
var msg = ex.ToString();
@@ -614,7 +614,7 @@ namespace ChanSort.Ui
this.currentPlugin = plugin;
this.currentTvSerializer = serializer;
this.DataRoot = serializer.DataRoot;
this.AddFileToMruList(tvDataFile);
this.AddFileToMruList(serializer.FileName);
this.UpdateMruMenu();
return true;

View File

@@ -1,5 +1,4 @@
using System;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;

View File

@@ -55,11 +55,11 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.3.0" newVersion="4.1.3.0" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />

View File

@@ -13,43 +13,30 @@ struct SHeader
uint32 channelBlockSize;
};
struct SChannel
struct SChannel_fta
{
uint32 curProgNr;
uint32 u1;
uint8 u2[8];
uint32 favNr;
union
{
char chName1[200];
#pragma byte_order (BigEndian)
big_endian wchar_t chName2[100];
#pragma byte_order ()
struct
{
uint8 zero;
wchar_t chName3[99];
uint8 zero2;
} chName4;
} chName;
char chName1[200];
uint16 u3;
uint8 u3b[208];
uint8 u3c[2];
uint16 u3d;
uint8 u4[10];
uint16 freqInMhz1;
uint16 u5;
uint32 freqInMhz1;
//uint16 u5;
uint16 u6;
uint16 symRate;
uint32 curProgNr2;
uint32 prevProgNr;
uint32 oldProgNr;
uint32 channelIndex;
uint16 tsid;
uint16 u7;
uint16 sid;
uint16 onid;
uint16 freqInMhz2;
uint16 u9;
uint32 u10;
uint8 padding[6];
};
struct SFooter
@@ -70,8 +57,105 @@ public struct Philips_mgr_chan_s_fta
SHeader header;
var recordCount = header.channelBlockSize / sizeof(SChannel);
SChannel channels[recordCount];
var recordCount = header.channelBlockSize / sizeof(SChannel_fta);
SChannel_fta channels[recordCount];
SFooter footer;
};
//#########################################################
struct SChannel_pkg
{
uint32 curProgNr;
uint32 u1;
uint8 u2[8];
uint32 favNr;
char chName1[200];
uint16 u3;
uint8 u3b[208];
uint8 u3c[2];
uint16 u3d;
uint8 u4[10];
uint32 freqInMhz1;
//uint16 u5;
uint16 u6;
uint16 symRate;
uint32 oldProgNr;
uint32 channelIndex;
uint16 tsid;
uint16 u7;
uint16 sid;
uint16 onid;
uint16 freqInMhz2;
uint8 padding[10];
};
public struct Philips_mgr_chan_s_pkg
{
var docSize = GetDocumentSize();
char filename[32];
SHeader header;
var recordCount = header.channelBlockSize / sizeof(SChannel_pkg);
SChannel_pkg channels[recordCount];
SFooter footer;
};
//#########################################################
struct CChannel
{
uint32 curProgNr;
uint32 u1;
uint8 u2[8];
uint32 favNr;
union
{
char chName1[200];
#pragma byte_order (BigEndian)
big_endian wchar_t chName2[100];
#pragma byte_order ()
struct
{
uint8 zero;
wchar_t chName3[99];
uint8 zero2;
} chName4;
} chName;
uint16 u3;
uint16 u3b;
char provider[200];
uint8 u4[16];
uint32 freqInHz;
uint16 u6;
uint16 not_symRate;
uint32 oldProgNr;
uint8 u7[4];
uint32 channelIndex;
uint16 tsid;
uint16 symRate_maybe;
uint16 sid;
uint16 onid;
//uint16 freqInMhz2;
//uint16 u9;
uint32 u10;
};
public struct Philips_mgr_chan_dvbt
{
var docSize = GetDocumentSize();
char filename[32];
SHeader header;
var recordCount = header.channelBlockSize / sizeof(CChannel);
CChannel channels[recordCount];
SFooter footer;
};

View File

@@ -1,5 +1,13 @@
ChanSort Change Log
===================
2021-09-06
- Philips: fixes for ChannelMap_100, 105 and 110 formats
- Philips: support for FLASH/*.bin DVB-T/C and preset DVB-S lists (mgr_chan_s_pkg.db)
- Toshiba: lists with chmgt_type001\\chmgt.bin can now be opened without zipping them
- Toshiba: selecting the hotelopt_type001.bin will now also load the list (if the type is supported)
- Alden: added support for "Alden" Android SmartTV channel list format (dvr_rtk_tv.db)
2021-09-05_2010
- Philips: fixed wrong .ini settings for formats 100, 105 and 110
@@ -11,7 +19,7 @@ ChanSort Change Log
- one that exports \*cmdb\*.bin files is now fully tested and working.
- ones that export only .xml files inside the channellib and s2channellib folders should work too, but not confirmed.
- Philips: ChannelFormat_105 and 110 specific settings in .ini, currently best-effort without user confirmation.
- Philips: added support for Repair\\Mgr_chan_s_fta.db lists. Can be read as a reference list, but editing is
- Philips: added support for Repair\\mgr_chan_s_fta.db lists. Can be read as a reference list, but editing is
currently disabled in the .ini file (enabling it is experimental)
- added experimental support for 8 variants of "dtv_cmdb_2.bin" DVB-S channel lists (DVB-C/T not supported yet).
Brands known to use this format include Sharp, Toshiba, Dyon, OK.