From 9c2a7b9195b446848d70d24a8669f1f08972c041 Mon Sep 17 00:00:00 2001 From: Horst Beham Date: Mon, 6 Sep 2021 20:42:45 +0200 Subject: [PATCH] - 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) --- .../ChanSort.Api/Controller/SerializerBase.cs | 3 + .../AldenSerializer.cs | 229 ++++++++++++++++++ .../ChanSort.Loader.Android/AndroidPlugin.cs | 23 ++ .../ChanSort.Loader.Android.csproj | 98 ++++++++ .../Properties/AssemblyInfo.cs | 36 +++ .../ChanSort.Loader.Android/packages.config | 13 + .../ChanSort.Loader.Panasonic/Serializer.cs | 3 +- .../ChanSort.Loader.Philips.ini | 83 ++++++- .../ChanSort.Loader.Philips/DbSerializer.cs | 129 ++++++---- .../ChanSort.Loader.Philips/PhilipsPlugin.cs | 2 +- .../ChanSort.Loader.Philips/XmlSerializer.cs | 9 +- .../ChmgtDbSerializer.cs | 23 +- .../ChanSort.Loader.Toshiba/ToshibaPlugin.cs | 56 ++++- source/ChanSort.sln | 32 +++ source/ChanSort/ChanSort.csproj | 8 + source/ChanSort/MainForm.cs | 4 +- source/ChanSort/Program.cs | 1 - source/ChanSort/app.config | 4 +- .../philips_mgr_chan_s_fta.db.h | 128 ++++++++-- source/changelog.md | 10 +- 20 files changed, 796 insertions(+), 98 deletions(-) create mode 100644 source/ChanSort.Loader.Android/AldenSerializer.cs create mode 100644 source/ChanSort.Loader.Android/AndroidPlugin.cs create mode 100644 source/ChanSort.Loader.Android/ChanSort.Loader.Android.csproj create mode 100644 source/ChanSort.Loader.Android/Properties/AssemblyInfo.cs create mode 100644 source/ChanSort.Loader.Android/packages.config diff --git a/source/ChanSort.Api/Controller/SerializerBase.cs b/source/ChanSort.Api/Controller/SerializerBase.cs index 010aa48..3a0bbb2 100644 --- a/source/ChanSort.Api/Controller/SerializerBase.cs +++ b/source/ChanSort.Api/Controller/SerializerBase.cs @@ -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 diff --git a/source/ChanSort.Loader.Android/AldenSerializer.cs b/source/ChanSort.Loader.Android/AldenSerializer.cs new file mode 100644 index 0000000..ea5a49b --- /dev/null +++ b/source/ChanSort.Loader.Android/AldenSerializer.cs @@ -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[] 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(); + 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() + /// + /// 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 + /// + 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 + } +} diff --git a/source/ChanSort.Loader.Android/AndroidPlugin.cs b/source/ChanSort.Loader.Android/AndroidPlugin.cs new file mode 100644 index 0000000..b6c198e --- /dev/null +++ b/source/ChanSort.Loader.Android/AndroidPlugin.cs @@ -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); + } + } +} diff --git a/source/ChanSort.Loader.Android/ChanSort.Loader.Android.csproj b/source/ChanSort.Loader.Android/ChanSort.Loader.Android.csproj new file mode 100644 index 0000000..53c7653 --- /dev/null +++ b/source/ChanSort.Loader.Android/ChanSort.Loader.Android.csproj @@ -0,0 +1,98 @@ + + + + + Debug + AnyCPU + {5088DB0D-6BDE-4678-8C50-A14E6A294A45} + Library + Properties + ChanSort.Loader.Android + ChanSort.Loader.Android + v4.8 + 512 + true + + + + + + true + full + false + ..\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + + ..\packages\Microsoft.Data.Sqlite.Core.5.0.8\lib\netstandard2.0\Microsoft.Data.Sqlite.dll + + + ..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.batteries_v2.dll + + + ..\packages\SQLitePCLRaw.core.2.0.4\lib\netstandard2.0\SQLitePCLRaw.core.dll + + + ..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.nativelibrary.dll + + + ..\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.0.4\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll + + + + ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + + + + + + + + + + + + + + {dccffa08-472b-4d17-bb90-8f513fc01392} + ChanSort.Api + + + + + + + + + + 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}. + + + + \ No newline at end of file diff --git a/source/ChanSort.Loader.Android/Properties/AssemblyInfo.cs b/source/ChanSort.Loader.Android/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ce30947 --- /dev/null +++ b/source/ChanSort.Loader.Android/Properties/AssemblyInfo.cs @@ -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")] diff --git a/source/ChanSort.Loader.Android/packages.config b/source/ChanSort.Loader.Android/packages.config new file mode 100644 index 0000000..ffda76e --- /dev/null +++ b/source/ChanSort.Loader.Android/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/ChanSort.Loader.Panasonic/Serializer.cs b/source/ChanSort.Loader.Panasonic/Serializer.cs index 2c9a868..ae19da4 100644 --- a/source/ChanSort.Loader.Panasonic/Serializer.cs +++ b/source/ChanSort.Loader.Panasonic/Serializer.cs @@ -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; diff --git a/source/ChanSort.Loader.Philips/ChanSort.Loader.Philips.ini b/source/ChanSort.Loader.Philips/ChanSort.Loader.Philips.ini index e18356c..e6ccf74 100644 --- a/source/ChanSort.Loader.Philips/ChanSort.Loader.Philips.ini +++ b/source/ChanSort.Loader.Philips/ChanSort.Loader.Philips.ini @@ -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 diff --git a/source/ChanSort.Loader.Philips/DbSerializer.cs b/source/ChanSort.Loader.Philips/DbSerializer.cs index eb46d0e..018ab1b 100644 --- a/source/ChanSort.Loader.Philips/DbSerializer.cs +++ b/source/ChanSort.Loader.Philips/DbSerializer.cs @@ -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 dataFilePaths = new List(); - 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 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 + 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 + { + "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 GetDataFilePaths() => this.dataFilePaths.ToList(); + public override IEnumerable 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); diff --git a/source/ChanSort.Loader.Philips/PhilipsPlugin.cs b/source/ChanSort.Loader.Philips/PhilipsPlugin.cs index 440f72d..b80fa51 100644 --- a/source/ChanSort.Loader.Philips/PhilipsPlugin.cs +++ b/source/ChanSort.Loader.Philips/PhilipsPlugin.cs @@ -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)."); } } } diff --git a/source/ChanSort.Loader.Philips/XmlSerializer.cs b/source/ChanSort.Loader.Philips/XmlSerializer.cs index b0703df..2263059 100644 --- a/source/ChanSort.Loader.Philips/XmlSerializer.cs +++ b/source/ChanSort.Loader.Philips/XmlSerializer.cs @@ -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(); diff --git a/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs b/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs index bf12671..2afac61 100644 --- a/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs +++ b/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs @@ -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 diff --git a/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs b/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs index 2e0e1a4..838e183 100644 --- a/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs +++ b/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs @@ -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); } } } diff --git a/source/ChanSort.sln b/source/ChanSort.sln index a490055..9bcc83e 100644 --- a/source/ChanSort.sln +++ b/source/ChanSort.sln @@ -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 diff --git a/source/ChanSort/ChanSort.csproj b/source/ChanSort/ChanSort.csproj index 5facc4a..f81804a 100644 --- a/source/ChanSort/ChanSort.csproj +++ b/source/ChanSort/ChanSort.csproj @@ -522,6 +522,14 @@ {DCCFFA08-472B-4D17-BB90-8F513FC01392} ChanSort.Api + + {5088db0d-6bde-4678-8c50-a14e6a294a45} + ChanSort.Loader.Android + + + {b594dda4-7bd5-450e-b648-668e0f659813} + ChanSort.Loader.CmdbBin + {4ad7f77e-617c-4741-82ae-e7a41c85ee4d} ChanSort.Loader.Enigma2 diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index 5e645db..09d7eb9 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -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; diff --git a/source/ChanSort/Program.cs b/source/ChanSort/Program.cs index 431209a..24a8f7e 100644 --- a/source/ChanSort/Program.cs +++ b/source/ChanSort/Program.cs @@ -1,5 +1,4 @@ using System; -using System.Drawing; using System.Globalization; using System.Threading; using System.Windows.Forms; diff --git a/source/ChanSort/app.config b/source/ChanSort/app.config index dd40c9c..bba8a51 100644 --- a/source/ChanSort/app.config +++ b/source/ChanSort/app.config @@ -55,11 +55,11 @@ - + - + diff --git a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/philips_mgr_chan_s_fta.db.h b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/philips_mgr_chan_s_fta.db.h index 94ce687..1ed7588 100644 --- a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/philips_mgr_chan_s_fta.db.h +++ b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/philips_mgr_chan_s_fta.db.h @@ -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; }; \ No newline at end of file diff --git a/source/changelog.md b/source/changelog.md index 39de986..ecfb73d 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -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.