diff --git a/source/ChanSort.Loader.Toshiba/ChanSort.Loader.Toshiba.csproj b/source/ChanSort.Loader.Toshiba/ChanSort.Loader.Toshiba.csproj index f85cdb2..4c337bc 100644 --- a/source/ChanSort.Loader.Toshiba/ChanSort.Loader.Toshiba.csproj +++ b/source/ChanSort.Loader.Toshiba/ChanSort.Loader.Toshiba.csproj @@ -25,6 +25,7 @@ 4 x86 false + latest pdbonly @@ -34,6 +35,7 @@ prompt 4 false + latest true @@ -45,6 +47,7 @@ false false false + latest ..\Release\ @@ -56,6 +59,7 @@ false false false + latest @@ -72,9 +76,10 @@ + - - + + diff --git a/source/ChanSort.Loader.Toshiba/DbSerializer.cs b/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs similarity index 98% rename from source/ChanSort.Loader.Toshiba/DbSerializer.cs rename to source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs index 053549c..4225fd7 100644 --- a/source/ChanSort.Loader.Toshiba/DbSerializer.cs +++ b/source/ChanSort.Loader.Toshiba/ChmgtDbSerializer.cs @@ -5,7 +5,7 @@ using ChanSort.Api; namespace ChanSort.Loader.Toshiba { - class DbSerializer : SerializerBase + class ChmgtDbSerializer : SerializerBase { private const string FILE_chmgt_db = "\\chmgt_type001\\chmgt.db"; private const string FILE_dvbSysData_db = "\\dvb_type001\\dvbSysData.db"; @@ -19,7 +19,7 @@ namespace ChanSort.Loader.Toshiba private readonly Dictionary channelInfoByUid = new Dictionary(); #region ctor() - public DbSerializer(string inputFile) : base(inputFile) + public ChmgtDbSerializer(string inputFile) : base(inputFile) { DepencencyChecker.AssertVc2010RedistPackageX86Installed(); diff --git a/source/ChanSort.Loader.Toshiba/DbSerializerPlugin.cs b/source/ChanSort.Loader.Toshiba/DbSerializerPlugin.cs deleted file mode 100644 index 7eaf51e..0000000 --- a/source/ChanSort.Loader.Toshiba/DbSerializerPlugin.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChanSort.Api; - -namespace ChanSort.Loader.Toshiba -{ - public class DbSerializerPlugin : ISerializerPlugin - { - public string DllName { get; set; } - public string PluginName => "Toshiba"; - public string FileFilter => "*.zip"; - - public SerializerBase CreateSerializer(string inputFile) - { - return new DbSerializer(inputFile); - } - } -} diff --git a/source/ChanSort.Loader.Toshiba/SettingsDbSerializer.cs b/source/ChanSort.Loader.Toshiba/SettingsDbSerializer.cs new file mode 100644 index 0000000..c9d6993 --- /dev/null +++ b/source/ChanSort.Loader.Toshiba/SettingsDbSerializer.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SQLite; +using System.IO; +using ChanSort.Api; + +namespace ChanSort.Loader.Toshiba +{ + /* + * This class loads Toshiba files stores as CLONE00001\settingsDB.db along with settingsDBBackup.db + * Currently only channel renaming, reordering and deletion is supported. + * We don't know yet how/where information about favorites and skip/lock/hide is stored. + * + * Also, there are SatTable and SatTxTable for satellites and transponders in the file, but it's unknown + * how these tables are linked with EASISerTable / DVBSerTable / ... + */ + class SettingsDbSerializer : SerializerBase + { + private readonly ChannelList channels = new ChannelList(SignalSource.All, "All"); + + #region ctor() + public SettingsDbSerializer(string inputFile) : base(inputFile) + { + DepencencyChecker.AssertVc2010RedistPackageX86Installed(); + + this.Features.ChannelNameEdit = ChannelNameEditMode.All; + this.Features.DeleteMode = DeleteMode.Physically; + this.Features.CanSkipChannels = false; + this.Features.CanLockChannels = false; + this.Features.CanHideChannels = false; + this.Features.SupportedFavorites = 0; + + this.DataRoot.AddChannelList(this.channels); + } + #endregion + + #region GetDataFilePaths() + public override IEnumerable GetDataFilePaths() + { + var list = new List(); + list.Add(this.FileName); + var backupFile = GetBackupFilePath(); + if (File.Exists(backupFile)) + list.Add(backupFile); + return list; + } + + private string GetBackupFilePath() + { + var dir = Path.GetDirectoryName(this.FileName); + var name = Path.GetFileNameWithoutExtension(this.FileName); + var ext = Path.GetExtension(this.FileName); + var backupFile = Path.Combine(dir, name + "Backup" + ext); + return backupFile; + } + + #endregion + + #region Load() + public override void Load() + { + string sysDataConnString = "Data Source=" + this.FileName; + using var conn = new SQLiteConnection(sysDataConnString); + conn.Open(); + using var cmd = conn.CreateCommand(); + + this.RepairCorruptedDatabaseImage(cmd); + + cmd.CommandText = "SELECT count(1) FROM sqlite_master WHERE type = 'table' and name='EASISerTable'"; + if (Convert.ToInt32(cmd.ExecuteScalar()) != 1) + throw new FileLoadException("File doesn't contain the expected tables"); + + this.ReadSatellites(cmd); + this.ReadTransponders(cmd); + this.ReadChannels(cmd); + } + #endregion + + #region RepairCorruptedDatabaseImage() + private void RepairCorruptedDatabaseImage(SQLiteCommand cmd) + { + cmd.CommandText = "REINDEX"; + cmd.ExecuteNonQuery(); + } + #endregion + + #region ReadSatellites() + private void ReadSatellites(SQLiteCommand cmd) + { + cmd.CommandText = "select m_id, m_name_serialized, m_orbital_position from SatTable"; + using var r = cmd.ExecuteReader(); + while (r.Read()) + { + Satellite sat = new Satellite(r.GetInt32(0)); + string eastWest = "E"; + int pos = r.GetInt32(2); + if (pos < 0) + { + pos = -pos; + eastWest = "W"; + } + sat.OrbitalPosition = $"{pos / 10}.{pos % 10}{eastWest}"; + sat.Name = r.GetString(1); + this.DataRoot.AddSatellite(sat); + } + } + #endregion + + #region ReadTransponders() + private void ReadTransponders(SQLiteCommand cmd) + { + cmd.CommandText = "select m_id, m_satellite_id, m_frequency, m_polarisation, m_symbol_rate from SatTxTable"; + using var r = cmd.ExecuteReader(); + while (r.Read()) + { + int id = r.GetInt32(0); + int satId = r.GetInt32(1); + int freq = r.GetInt32(2); + + if (this.DataRoot.Transponder.TryGet(id) != null) + continue; + Transponder tp = new Transponder(id); + tp.FrequencyInMhz = (decimal)freq; + tp.Polarity = r.GetInt32(3) == 0 ? 'H' : 'V'; + tp.Satellite = this.DataRoot.Satellites.TryGet(satId); + tp.SymbolRate = r.GetInt32(4); + this.DataRoot.AddTransponder(tp.Satellite, tp); + } + } + #endregion + + #region ReadChannels() + private void ReadChannels(SQLiteCommand cmd) + { + int ixE = 0; + int ixD = 3; + int ixA = 8; + int ixT = 9; + int ixDC = 10; + + cmd.CommandText = @" +select + e.m_handle, e.m_rsn, e.m_name_serialized, + d.m_onid, d.m_tsid, d.m_id, d.m_type, d.m_name_serialized, + a.m_name_serialized, + t.frequency_in_multiples_of_10Hz, + dc.frequency +from EASISerTable e +left outer join DVBSerTable d on d.m_handle=e.m_handle +left outer join AnalogSerTable a on a.m_handle=e.m_handle +left outer join ChanDataTable dc on dc.handle=d.m_channel_no +left outer join ChanDataTable ac on ac.handle=a.m_channel_no +left outer join TADTunerDataTable t on t.channel=ac.channel_no"; + using var r = cmd.ExecuteReader(); + while (r.Read()) + { + var handle = r.GetInt32(ixE + 0); + var oldProgNr = r.GetInt32(ixE + 1); + var name = r.GetString(ixE + 2); + ChannelInfo channel = new ChannelInfo(0, handle, oldProgNr, name); + + // DVB + if (!r.IsDBNull(ixD + 0)) + { + channel.OriginalNetworkId = r.GetInt32(ixD + 0) & 0x1FFF; + channel.TransportStreamId = r.GetInt32(ixD + 1) & 0x1FFF; + channel.ServiceId = r.GetInt32(ixD + 2) & 0x1FFF; + channel.ServiceType = r.GetInt32(ixD + 3); + channel.FreqInMhz = (decimal) r.GetInt32(ixDC + 0) / 1000; + } + + // analog + if (!r.IsDBNull(ixA + 0)) + { + channel.FreqInMhz = (decimal) r.GetInt32(ixT + 0) / 100000; + } + + if (!channel.IsDeleted) + this.DataRoot.AddChannel(this.channels, channel); + } + } + #endregion + + + #region Save() + public override void Save(string tvOutputFile) + { + if (tvOutputFile != this.FileName) + { + File.Copy(this.FileName, tvOutputFile, true); + this.FileName = tvOutputFile; + } + + string channelConnString = "Data Source=" + this.FileName; + using var conn = new SQLiteConnection(channelConnString); + conn.Open(); + using var cmd = conn.CreateCommand(); + using var cmd2 = conn.CreateCommand(); + using var trans = conn.BeginTransaction(); + + this.WriteChannels(cmd, cmd2, this.channels); + trans.Commit(); + + this.RepairCorruptedDatabaseImage(cmd); + conn.Close(); + + // copy settingsDB.db to settingsDBBackup.db + var backupFile = GetBackupFilePath(); + File.Copy(this.FileName, backupFile, true); + } + #endregion + + #region WriteChannels() + private void WriteChannels(SQLiteCommand cmd, SQLiteCommand cmdDelete, ChannelList channelList) + { + cmd.CommandText = "update EASISerTable set m_rsn=@nr, m_name_serialized=@name where m_handle=@handle"; + cmd.Parameters.Add(new SQLiteParameter("@handle", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@nr", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@name", DbType.String)); + cmd.Prepare(); + + cmdDelete.CommandText = @" +delete from EASISerTable where m_handle=@handle; +delete from DVBSerTable where m_handle=@handle; +delete from AnalogSerTable where m_handle=@handle; +"; + cmdDelete.Parameters.Add(new SQLiteParameter("@handle", DbType.Int32)); + + foreach (ChannelInfo channel in channelList.Channels) + { + if (channel.IsProxy) // ignore reference list proxy channels + continue; + + if (channel.IsDeleted) + { + cmdDelete.Parameters["@handle"].Value = channel.RecordIndex; + cmdDelete.ExecuteNonQuery(); + } + else + { + channel.UpdateRawData(); + cmd.Parameters["@handle"].Value = channel.RecordIndex; + cmd.Parameters["@nr"].Value = channel.NewProgramNr; + cmd.Parameters["@name"].Value = channel.Name; + cmd.ExecuteNonQuery(); + } + } + } + #endregion + } +} diff --git a/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs b/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs new file mode 100644 index 0000000..b2262e7 --- /dev/null +++ b/source/ChanSort.Loader.Toshiba/ToshibaPlugin.cs @@ -0,0 +1,20 @@ +using System.IO; +using ChanSort.Api; + +namespace ChanSort.Loader.Toshiba +{ + public class ToshibaPlugin : ISerializerPlugin + { + public string DllName { get; set; } + public string PluginName => "Toshiba"; + public string FileFilter => "*.zip;*.db"; + + public SerializerBase CreateSerializer(string inputFile) + { + if (Path.GetExtension(inputFile).ToLower() == ".db") + return new SettingsDbSerializer(inputFile); + else + return new ChmgtDbSerializer(inputFile); + } + } +} diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index d1aa2d3..9a15266 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -534,7 +534,8 @@ namespace ChanSort.Ui catch (Exception ex) { serializer?.Dispose(); - errorMsgs.AppendLine($"{plugin.DllName} ({plugin.PluginName}): {ex}\n\n"); + 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"); if (ex is ArgumentException) { var msg = ex.ToString(); diff --git a/source/Test.Loader.Toshiba/ToshibaChmgtDbTest.cs b/source/Test.Loader.Toshiba/ToshibaChmgtDbTest.cs index 38161b6..32a008b 100644 --- a/source/Test.Loader.Toshiba/ToshibaChmgtDbTest.cs +++ b/source/Test.Loader.Toshiba/ToshibaChmgtDbTest.cs @@ -34,7 +34,7 @@ namespace Test.Loader.Toshiba private void TestChannelsAddedToCorrectLists(string fileName, SignalSource signalSource, int expectedTv, int expectedRadio, int dataProgramSid = 0, string dataProgramName = null) { var tempFile = TestUtils.DeploymentItem("Test.Loader.Toshiba\\TestFiles\\" + fileName); - var plugin = new DbSerializerPlugin(); + var plugin = new ToshibaPlugin(); var ser = plugin.CreateSerializer(tempFile); ser.Load(); @@ -72,7 +72,7 @@ namespace Test.Loader.Toshiba public void TestDeletingChannel() { var tempFile = TestUtils.DeploymentItem("Test.Loader.Toshiba\\TestFiles\\Toshiba-SL863G.zip"); - var plugin = new DbSerializerPlugin(); + var plugin = new ToshibaPlugin(); var ser = plugin.CreateSerializer(tempFile); ser.Load(); var data = ser.DataRoot; diff --git a/source/changelog.md b/source/changelog.md index df2e7bf..a3f0b59 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -1,6 +1,9 @@ ChanSort Change Log =================== +2021-01-23 +- Toshiba: added support for settingsDB.db lists + 2021-01-17 - Philips: added support for ChannelMap_45 format - Philips: fixed display of symbol rate and frequency (off by factor 1000 depending of list and DVB source)