diff --git a/ChanSort.Loader.Panasonic/Serializer.cs b/ChanSort.Loader.Panasonic/Serializer.cs index 880c49b..00a2032 100644 --- a/ChanSort.Loader.Panasonic/Serializer.cs +++ b/ChanSort.Loader.Panasonic/Serializer.cs @@ -10,31 +10,310 @@ namespace ChanSort.Loader.Panasonic { class Serializer : SerializerBase { + private static readonly string ERR_FileFormatOrEncryption = "File uses an unknown format or encryption"; + private static readonly int[] headerCypherTable; private readonly ChannelList atvChannels = new ChannelList(SignalSource.AnalogCT | SignalSource.Tv, "Analog TV"); private readonly ChannelList dtvTvChannels = new ChannelList(SignalSource.DvbCT | SignalSource.DvbS | SignalSource.Tv, "Digital TV"); private readonly ChannelList dtvRadioChannels = new ChannelList(SignalSource.DvbCT | SignalSource.DvbS | SignalSource.Radio, "Radio"); - private string tempFile; + private string workFile; + private CypherMode cypherMode; + private byte[] fileHeader = new byte[0]; + private int dbSizeOffset; + enum CypherMode + { + None, + HeaderAndChecksum, + Encryption, + Unknown + } + + + #region static ctor / headerCypherTable + static Serializer() + { + headerCypherTable = new[] + { + 0, + 79764919, + 159529838, + 222504665, + 319059676, + 398814059, + 445009330, + 507990021, + 638119352, + 583659535, + 797628118, + 726387553, + 890018660, + 835552979, + 1015980042, + 944750013, + 1276238704, + 1221641927, + 1167319070, + 1095957929, + 1595256236, + 1540665371, + 1452775106, + 1381403509, + 1780037320, + 1859660671, + 1671105958, + 1733955601, + 2031960084, + 2111593891, + 1889500026, + 1952343757, + -1742489888, + -1662866601, + -1851683442, + -1788833735, + -1960329156, + -1880695413, + -2103051438, + -2040207643, + -1104454824, + -1159051537, + -1213636554, + -1284997759, + -1389417084, + -1444007885, + -1532160278, + -1603531939, + -734892656, + -789352409, + -575645954, + -646886583, + -952755380, + -1007220997, + -827056094, + -898286187, + -231047128, + -151282273, + -71779514, + -8804623, + -515967244, + -436212925, + -390279782, + -327299027, + 881225847, + 809987520, + 1023691545, + 969234094, + 662832811, + 591600412, + 771767749, + 717299826, + 311336399, + 374308984, + 453813921, + 533576470, + 25881363, + 88864420, + 134795389, + 214552010, + 2023205639, + 2086057648, + 1897238633, + 1976864222, + 1804852699, + 1867694188, + 1645340341, + 1724971778, + 1587496639, + 1516133128, + 1461550545, + 1406951526, + 1302016099, + 1230646740, + 1142491917, + 1087903418, + -1398421865, + -1469785312, + -1524105735, + -1578704818, + -1079922613, + -1151291908, + -1239184603, + -1293773166, + -1968362705, + -1905510760, + -2094067647, + -2014441994, + -1716953613, + -1654112188, + -1876203875, + -1796572374, + -525066777, + -462094256, + -382327159, + -302564546, + -206542021, + -143559028, + -97365931, + -17609246, + -960696225, + -1031934488, + -817968335, + -872425850, + -709327229, + -780559564, + -600130067, + -654598054, + 1762451694, + 1842216281, + 1619975040, + 1682949687, + 2047383090, + 2127137669, + 1938468188, + 2001449195, + 1325665622, + 1271206113, + 1183200824, + 1111960463, + 1543535498, + 1489069629, + 1434599652, + 1363369299, + 622672798, + 568075817, + 748617968, + 677256519, + 907627842, + 853037301, + 1067152940, + 995781531, + 51762726, + 131386257, + 177728840, + 240578815, + 269590778, + 349224269, + 429104020, + 491947555, + -248556018, + -168932423, + -122852000, + -60002089, + -500490030, + -420856475, + -341238852, + -278395381, + -685261898, + -739858943, + -559578920, + -630940305, + -1004286614, + -1058877219, + -845023740, + -916395085, + -1119974018, + -1174433591, + -1262701040, + -1333941337, + -1371866206, + -1426332139, + -1481064244, + -1552294533, + -1690935098, + -1611170447, + -1833673816, + -1770699233, + -2009983462, + -1930228819, + -2119160460, + -2056179517, + 1569362073, + 1498123566, + 1409854455, + 1355396672, + 1317987909, + 1246755826, + 1192025387, + 1137557660, + 2072149281, + 2135122070, + 1912620623, + 1992383480, + 1753615357, + 1816598090, + 1627664531, + 1707420964, + 295390185, + 358241886, + 404320391, + 483945776, + 43990325, + 106832002, + 186451547, + 266083308, + 932423249, + 861060070, + 1041341759, + 986742920, + 613929101, + 542559546, + 756411363, + 701822548, + -978770311, + -1050133554, + -869589737, + -924188512, + -693284699, + -764654318, + -550540341, + -605129092, + -475935807, + -413084042, + -366743377, + -287118056, + -257573603, + -194731862, + -114850189, + -35218492, + -1984365303, + -1921392450, + -2143631769, + -2063868976, + -1698919467, + -1635936670, + -1824608069, + -1744851700, + -1347415887, + -1418654458, + -1506661409, + -1561119128, + -1129027987, + -1200260134, + -1254728445, + -1309196108 + }; + } + #endregion + + #region ctor() public Serializer(string inputFile) : base(inputFile) { this.DataRoot.AddChannelList(this.atvChannels); this.DataRoot.AddChannelList(this.dtvTvChannels); this.DataRoot.AddChannelList(this.dtvRadioChannels); } + #endregion - public override string DisplayName { get { return "Panasonic .db Loader"; } } + public override string DisplayName { get { return "Panasonic .db/.bin Loader"; } } #region Load() public override void Load() { - this.tempFile = Path.GetTempFileName(); - Application.ApplicationExit += CleanTempFile; - this.CypherFile(this.FileName, this.tempFile); + this.workFile = this.GetUncypheredWorkFile(); this.CreateDummySatellites(); - string channelConnString = "Data Source=" + this.tempFile; + string channelConnString = "Data Source=" + this.workFile; using (var conn = new SQLiteConnection(channelConnString)) { conn.Open(); @@ -46,6 +325,41 @@ namespace ChanSort.Loader.Panasonic } #endregion + #region GetUncypheredWorkFile() + private string GetUncypheredWorkFile() + { + this.cypherMode = this.GetCypherMode(this.FileName); + if (cypherMode == CypherMode.Unknown) + throw new FileLoadException(ERR_FileFormatOrEncryption); + if (cypherMode == CypherMode.None) + return this.FileName; + + var tempFile = this.FileName + ".tmp"; + File.Delete(tempFile); + Application.ApplicationExit += CleanTempFile; + if (cypherMode == CypherMode.Encryption) + this.CypherFile(this.FileName, tempFile); + else + this.RemoveHeader(this.FileName, tempFile); + return tempFile; + } + #endregion + + #region GetCypherMode() + private CypherMode GetCypherMode(string file) + { + using (var stream = File.OpenRead(file)) + using (var rdr = new BinaryReader(stream)) + { + uint value = (uint)rdr.ReadInt32(); + if (value == 0x694C5153) return CypherMode.None; // "SQLi" + if (value == 0x42445350) return CypherMode.HeaderAndChecksum; // "PSDB" + if (value == 0xA07DCB50) return CypherMode.Encryption; + return CypherMode.Unknown; + } + } + #endregion + #region CypherFile() /// /// XOR-based cypher which can be used to alternately crypt/decrypt data @@ -71,10 +385,45 @@ namespace ChanSort.Loader.Panasonic } #endregion + #region RemoveHeader() + private void RemoveHeader(string inputFile, string outputFile) + { + var data = File.ReadAllBytes(inputFile); + if (this.CalcPsdbChecksum(data, data.Length) != 0) + throw new FileLoadException("Checksum validation failed"); + + int offset = 30 + (data[28] << 8) + data[29]; + this.dbSizeOffset = offset; + int dbSize = (data[offset] << 24) + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3]; + offset += 4; + if (data.Length != offset + dbSize + 4) + throw new FileLoadException("File size validation failed"); + + using (var stream = new FileStream(outputFile, FileMode.Create, FileAccess.Write)) + stream.Write(data, offset, data.Length - offset - 4); + + this.fileHeader = new byte[offset]; + Array.Copy(data, 0, this.fileHeader, 0, offset); + } + #endregion + + #region CalcPsdbChecksum() + private uint CalcPsdbChecksum(byte[] data, int length) + { + uint v = 0xffffffff; + for (int i = 0; i < length; i++) + { + byte b = data[i]; + v = (v << 8) ^ (uint)headerCypherTable[((v >> 24) ^ b) & 0xFF]; + } + return v; + } + #endregion + #region CleanTempFile() private void CleanTempFile(object sender, EventArgs e) { - try { File.Delete(this.tempFile);} + try { File.Delete(this.workFile);} catch { } } #endregion @@ -98,10 +447,13 @@ namespace ChanSort.Loader.Panasonic string[] fieldNames = { "rowid", "major_channel", "physical_ch","sname", "freq", "skip", "running_status","free_CA_mode","child_lock", "profile1index","profile2index","profile3index","profile4index","stype", "onid", "tsid", "sid", "ntype", "delivery" }; - const string sql = "select s.rowid,s.major_channel,s.physical_ch,s.sname,t.freq,s.skip,s.running_status,s.free_CA_mode,s.child_lock, " + - "profile1index,profile2index,profile3index,profile4index,s.stype,s.onid,s.tsid,s.svcid,s.ntype,delivery" + - " from SVL s left outer join TSL t on s.ntype=t.ntype and s.physical_ch=t.physical_ch and s.tsid=t.tsid"+ - " order by s.ntype,major_channel"; + const string sql = @" +select s.rowid,s.major_channel,s.physical_ch,s.sname,t.freq,s.skip,s.running_status,s.free_CA_mode,s.child_lock, + profile1index,profile2index,profile3index,profile4index,s.stype,s.onid,s.tsid,s.svcid,s.ntype,delivery +from SVL s +left outer join TSL t on s.ntype=t.ntype and s.physical_ch=t.physical_ch and s.tsid=t.tsid +order by s.ntype,major_channel +"; var fields = this.GetFieldMap(fieldNames); @@ -122,21 +474,6 @@ namespace ChanSort.Loader.Panasonic } #endregion - #region GetQuery() - private string GetQuery(string table, string[] fieldNames) - { - string sql = "select "; - for (int i = 0; i < fieldNames.Length; i++) - { - if (i > 0) - sql += ","; - sql += fieldNames[i]; - } - sql += " from " + table; - return sql; - } - #endregion - #region GetFieldMap() private IDictionary GetFieldMap(string[] fieldNames) { @@ -148,12 +485,13 @@ namespace ChanSort.Loader.Panasonic #endregion + #region Save() public override void Save(string tvOutputFile) { this.FileName = tvOutputFile; - string channelConnString = "Data Source=" + this.tempFile; + string channelConnString = "Data Source=" + this.workFile; using (var conn = new SQLiteConnection(channelConnString)) { conn.Open(); @@ -161,36 +499,104 @@ namespace ChanSort.Loader.Panasonic { using (var trans = conn.BeginTransaction()) { - this.WriteChannels(cmd, this.atvChannels); - this.WriteChannels(cmd, this.dtvTvChannels); - this.WriteChannels(cmd, this.dtvRadioChannels); + int[] channelIndex = new int[5]; + this.WriteChannels(cmd, this.atvChannels, channelIndex); + this.WriteChannels(cmd, this.dtvTvChannels, channelIndex); + this.WriteChannels(cmd, this.dtvRadioChannels, channelIndex); trans.Commit(); } } } - this.CypherFile(this.tempFile, this.FileName); + this.WriteCypheredFile(); } #endregion #region WriteChannels() - private void WriteChannels(SQLiteCommand cmd, ChannelList channelList) + private void WriteChannels(SQLiteCommand cmd, ChannelList channelList, int[] channelIndex) { - cmd.CommandText = "update SVL set ... child_lock=@lock, skip=@skip where rowid=@rowid"; + cmd.CommandText = "update SVL set major_channel=@progNr, sname=@name, profile1index=@fav1, profile2index=@fav2, profile3index=@fav3, profile4index=@fav4, child_lock=@lock, skip=@skip where rowid=@rowid"; + cmd.Parameters.Clear(); cmd.Parameters.Add(new SQLiteParameter("@rowid", DbType.Int32)); - cmd.Parameters.Add(new SQLiteParameter("@child_lock", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@progNr", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@fav1", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@fav2", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@fav3", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@fav4", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@name", DbType.String)); + cmd.Parameters.Add(new SQLiteParameter("@lock", DbType.Int32)); cmd.Parameters.Add(new SQLiteParameter("@skip", DbType.Int32)); cmd.Prepare(); foreach (DbChannel channel in channelList.Channels) { + if (channel.NewProgramNr < 0) + continue; channel.UpdateRawData(); cmd.Parameters["@rowid"].Value = channel.RecordIndex; - cmd.Parameters["@child_lock"].Value = channel.Lock; + cmd.Parameters["@progNr"].Value = ++channelIndex[0]; // channel.NewProgramNr; + for (int fav = 0; fav < 4; fav++) + cmd.Parameters["@fav" + (fav + 1)].Value = ((int) channel.Favorites & (1<= 0) + continue; + cmd.Parameters["@rowid"].Value = channel.RecordIndex; + cmd.ExecuteNonQuery(); + } } #endregion + #region WriteCypheredFile() + private void WriteCypheredFile() + { + switch (this.cypherMode) + { + case CypherMode.None: + break; + case CypherMode.Encryption: + this.CypherFile(this.workFile, this.FileName); + break; + case CypherMode.HeaderAndChecksum: + this.WriteFileWithHeaderAndChecksum(); + break; + } + } + #endregion + + #region WriteFileWithHeaderAndChecksum() + private void WriteFileWithHeaderAndChecksum() + { + long workFileSize = new FileInfo(this.workFile).Length; + byte[] data = new byte[this.fileHeader.Length + workFileSize + 4]; + Array.Copy(fileHeader, data, fileHeader.Length); + using (var stream = new FileStream(this.workFile, FileMode.Open, FileAccess.Read)) + stream.Read(data, fileHeader.Length, (int)workFileSize); + + data[this.dbSizeOffset + 0] = (byte)(workFileSize >> 24); + data[this.dbSizeOffset + 1] = (byte)(workFileSize >> 16); + data[this.dbSizeOffset + 2] = (byte)(workFileSize >> 8); + data[this.dbSizeOffset + 3] = (byte)(workFileSize); + + uint checksum = this.CalcPsdbChecksum(data, data.Length - 4); + data[data.Length - 1] = (byte)(checksum & 0xFF); + data[data.Length - 2] = (byte)((checksum >> 8) & 0xFF); + data[data.Length - 3] = (byte)((checksum >> 16) & 0xFF); + data[data.Length - 4] = (byte)((checksum >> 24) & 0xFF); + + using (var stream = new FileStream(this.FileName, FileMode.Create, FileAccess.Write)) + stream.Write(data, 0, data.Length); + } + + #endregion } } diff --git a/ChanSort.Loader.Panasonic/SerializerPlugin.cs b/ChanSort.Loader.Panasonic/SerializerPlugin.cs index 0456fe5..1f879cc 100644 --- a/ChanSort.Loader.Panasonic/SerializerPlugin.cs +++ b/ChanSort.Loader.Panasonic/SerializerPlugin.cs @@ -4,8 +4,8 @@ namespace ChanSort.Loader.Panasonic { public class SerializerPlugin : ISerializerPlugin { - public string PluginName { get { return "Panasonic *.db"; } } - public string FileFilter { get { return "*.db"; } } + public string PluginName { get { return "Panasonic *.db,*.bin"; } } + public string FileFilter { get { return "*.db;*.bin"; } } public SerializerBase CreateSerializer(string inputFile) { diff --git a/ChanSort.sln b/ChanSort.sln index a5c2fb6..69b8049 100644 --- a/ChanSort.sln +++ b/ChanSort.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort", "ChanSort\ChanSort.csproj", "{5FAFDABC-A52F-498C-BD2F-AFFC4119797A}" ProjectSection(ProjectDependencies) = postProject + {68DA8072-3A29-4076-9F64-D66F38349585} = {68DA8072-3A29-4076-9F64-D66F38349585} {A1C9A98D-368A-44E8-9B7F-7EACA46C9EC5} = {A1C9A98D-368A-44E8-9B7F-7EACA46C9EC5} + {F6F02792-07F1-48D5-9AF3-F945CA5E3931} = {F6F02792-07F1-48D5-9AF3-F945CA5E3931} {E972D8A1-2F5F-421C-AC91-CFF45E5191BE} = {E972D8A1-2F5F-421C-AC91-CFF45E5191BE} EndProjectSection EndProject diff --git a/ChanSort/MainForm.cs b/ChanSort/MainForm.cs index 039e7a2..47a0389 100644 --- a/ChanSort/MainForm.cs +++ b/ChanSort/MainForm.cs @@ -24,7 +24,7 @@ namespace ChanSort.Ui { public partial class MainForm : XtraForm { - public const string AppVersion = "v2013-06-24"; + public const string AppVersion = "v2013-06-25"; private const int MaxMruEntries = 5; diff --git a/readme.txt b/readme.txt index 839395e..15b9e2d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,12 +1,7 @@ -Version v2013-06-24 ====================================================== +Version v2013-06-25 ====================================================== Changes: -- FIX: Resizing a column caused an exception -- FIX: Deleting satellite channels from an SCM file did not work correctly -- Improved SCM file format detection -- Samsung E/F-Series: channels in the favorite lists now use their prog# - instead of all being put at #1 - (in a future version the fav lists may be sorted separately) +- Added experimental support for Panasonic channel lists (svl.db, svl.bin) The complete change log can be found at the end of this document @@ -61,7 +56,9 @@ LG https://sourceforge.net/p/chansort/wiki/Home/ http://www.ullrich.es/job/service-menue/lg-tlledit-lg-sendersortierung/ - +Panasonic +------- + Models with svl.bin/svl.db channel lists Toshiba ------- @@ -104,6 +101,9 @@ OTHER DEALINGS IN THE SOFTWARE. Change log ================================================================ +2013-06-25 +- Added experimental support for Panasonic channel lists (svl.db, svl.bin) + 2013-06-24 - FIX: Resizing a column caused an exception - FIX: Deleting satellite channels from an SCM file did not work correctly