diff --git a/source/ChanSort.Api/Model/ChannelInfo.cs b/source/ChanSort.Api/Model/ChannelInfo.cs index a37e383..266cb2f 100644 --- a/source/ChanSort.Api/Model/ChannelInfo.cs +++ b/source/ChanSort.Api/Model/ChannelInfo.cs @@ -5,8 +5,6 @@ namespace ChanSort.Api { public class ChannelInfo { - public const int MAX_FAV_LISTS = 16; - private string uid; private string serviceTypeName; private int newProgramNr; @@ -106,13 +104,8 @@ namespace ChanSort.Api { this.OldProgramNr = -1; this.NewProgramNr = -1; - this.FavIndex = new List(MAX_FAV_LISTS); - this.OldFavIndex = new List(MAX_FAV_LISTS); - for (int i = 0; i < MAX_FAV_LISTS; i++) - { - this.FavIndex.Add(-1); - this.OldFavIndex.Add(-1); - } + this.FavIndex = new List(); + this.OldFavIndex = new List(); this.Name = ""; this.ShortName = ""; } @@ -315,7 +308,7 @@ namespace ChanSort.Api /// public int GetPosition(int subListIndex) { - return subListIndex == 0 ? this.NewProgramNr : this.FavIndex[subListIndex - 1]; + return subListIndex < 0 ? -1 : subListIndex == 0 ? this.NewProgramNr : subListIndex - 1 < this.FavIndex.Count ? this.FavIndex[subListIndex - 1] : -1; } /// @@ -323,7 +316,7 @@ namespace ChanSort.Api /// public int GetOldPosition(int subListIndex) { - return subListIndex == 0 ? this.OldProgramNr : this.OldFavIndex[subListIndex - 1]; + return subListIndex < 0 ? -1 : subListIndex == 0 ? this.OldProgramNr : subListIndex - 1 < this.OldFavIndex.Count ? this.OldFavIndex[subListIndex - 1] : -1; } /// @@ -331,10 +324,14 @@ namespace ChanSort.Api /// public void SetPosition(int subListIndex, int newPos) { + if (subListIndex < 0) + return; if (subListIndex == 0) this.NewProgramNr = newPos; else { + for (int i=this.FavIndex.Count; i<=subListIndex;i++) + this.FavIndex.Add(-1); this.FavIndex[subListIndex - 1] = newPos; int mask = 1 << (subListIndex - 1); if (newPos == -1) @@ -349,10 +346,16 @@ namespace ChanSort.Api /// public void SetOldPosition(int subListIndex, int oldPos) { + if (subListIndex < 0) + return; if (subListIndex == 0) this.OldProgramNr = oldPos; else + { + for (int i = this.OldFavIndex.Count; i <= subListIndex; i++) + this.OldFavIndex.Add(-1); this.OldFavIndex[subListIndex - 1] = oldPos; + } } /// @@ -363,7 +366,11 @@ namespace ChanSort.Api if (subListIndex == 0) this.NewProgramNr += delta; else - this.FavIndex[subListIndex - 1] += delta; + { + for (int i = this.FavIndex.Count; i <= subListIndex; i++) + this.FavIndex.Add(-1); + this.FavIndex[subListIndex - 1] += delta; + } } #endregion } diff --git a/source/ChanSort.Api/Model/ChannelList.cs b/source/ChanSort.Api/Model/ChannelList.cs index cce962c..94e40c0 100644 --- a/source/ChanSort.Api/Model/ChannelList.cs +++ b/source/ChanSort.Api/Model/ChannelList.cs @@ -12,6 +12,8 @@ namespace ChanSort.Api private int insertProgramNr = 1; private int duplicateUidCount; private int duplicateProgNrCount; + public int FavListCount { get; set; } + public static List DefaultVisibleColumns { get; set; } = new List(); // initialized by MainForm diff --git a/source/ChanSort.Api/Model/DataRoot.cs b/source/ChanSort.Api/Model/DataRoot.cs index 3b5cc63..bab56d7 100644 --- a/source/ChanSort.Api/Model/DataRoot.cs +++ b/source/ChanSort.Api/Model/DataRoot.cs @@ -17,7 +17,6 @@ namespace ChanSort.Api public bool IsEmpty => this.channelLists.Count == 0; public bool NeedsSaving { get; set; } - public Favorites SupportedFavorites => this.loader.Features.SupportedFavorites; public bool SortedFavorites => this.loader.Features.SortedFavorites; public bool MixedSourceFavorites => this.loader.Features.MixedSourceFavorites; @@ -111,6 +110,12 @@ namespace ChanSort.Api { foreach (var list in this.ChannelLists) { + if (list.FavListCount == 0) + { + for (ulong m = (ulong) this.loader.Features.SupportedFavorites; m != 0; m >>= 1) + ++list.FavListCount; + } + if (list.IsMixedSourceFavoritesList) { loader.Features.SortedFavorites = true; // all mixed source favorite lists must support ordering @@ -142,7 +147,7 @@ namespace ChanSort.Api int c = 0; if (this.MixedSourceFavorites || this.SortedFavorites) { - for (int m = (int) this.SupportedFavorites; m != 0; m >>= 1) + for (ulong m = (ulong) this.SupportedFavorites; m != 0; m >>= 1) ++c; } @@ -265,7 +270,7 @@ namespace ChanSort.Api var hasCaption = favListCaptions.TryGetValue(favIndex, out var caption); if (!asTabCaption) return caption; - var letter = (char)('A' + favIndex); + var letter = favIndex < 26 ? ((char)('A' + favIndex)).ToString() : (favIndex + 1).ToString(); return hasCaption && !string.IsNullOrEmpty(caption) ? letter + ": " + caption : "Fav " + letter; } #endregion diff --git a/source/ChanSort.Api/Model/Enums.cs b/source/ChanSort.Api/Model/Enums.cs index b4c9923..ac9b311 100644 --- a/source/ChanSort.Api/Model/Enums.cs +++ b/source/ChanSort.Api/Model/Enums.cs @@ -76,7 +76,7 @@ namespace ChanSort.Api #endregion [Flags] - public enum Favorites : byte { A = 0x01, B = 0x02, C = 0x04, D = 0x08, E = 0x10, F=0x20, G=0x40, H=0x80 } + public enum Favorites : long { A = 0x01, B = 0x02, C = 0x04, D = 0x08, E = 0x10, F=0x20, G=0x40, H=0x80 } public enum UnsortedChannelMode { diff --git a/source/ChanSort.Loader.Enigma2/ChanSort.Loader.Enigma2.csproj b/source/ChanSort.Loader.Enigma2/ChanSort.Loader.Enigma2.csproj new file mode 100644 index 0000000..e654955 --- /dev/null +++ b/source/ChanSort.Loader.Enigma2/ChanSort.Loader.Enigma2.csproj @@ -0,0 +1,77 @@ + + + + + Debug + AnyCPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D} + Library + Properties + ChanSort.Loader.Enigma2 + ChanSort.Loader.Enigma2 + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + latest + prompt + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + latest + prompt + + + + + + + + + + + + + + + + + + + + {dccffa08-472b-4d17-bb90-8f513fc01392} + ChanSort.Api + + + + \ No newline at end of file diff --git a/source/ChanSort.Loader.Enigma2/Channel.cs b/source/ChanSort.Loader.Enigma2/Channel.cs new file mode 100644 index 0000000..cca83db --- /dev/null +++ b/source/ChanSort.Loader.Enigma2/Channel.cs @@ -0,0 +1,29 @@ +using ChanSort.Api; + +namespace ChanSort.Loader.Enigma2 +{ + internal class Channel : ChannelInfo + { + /// + /// first two fields of the lamedb entry + /// + public string Prefix { get; set; } + + /// + /// For DVB-S it is the orbital position * 10 (e.g. 192 for Astra 19.2E) * 65536 + /// + public int DvbNamespace { get; set; } + + public int ServiceNumber { get; set; } + + /// + /// all fields after the DVB-namespace in the lamedb entry + /// + public string Suffix { get; set; } + + /// + /// #DESCRIPTION of the userbouquet entry + /// + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs b/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs new file mode 100644 index 0000000..3472fa0 --- /dev/null +++ b/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs @@ -0,0 +1,16 @@ +using ChanSort.Api; + +namespace ChanSort.Loader.Enigma2 +{ + public class Enigma2Plugin : ISerializerPlugin + { + public string DllName { get; set; } + public string PluginName => "Enigma2 (Linux Receiver)"; + public string FileFilter => "bouquets.*"; + + public SerializerBase CreateSerializer(string inputFile) + { + return new Serializer(inputFile); + } + } +} diff --git a/source/ChanSort.Loader.Enigma2/Properties/AssemblyInfo.cs b/source/ChanSort.Loader.Enigma2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9830ddc --- /dev/null +++ b/source/ChanSort.Loader.Enigma2/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.Enigma2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ChanSort.Loader.Enigma2")] +[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("4ad7f77e-617c-4741-82ae-e7a41c85ee4d")] + +// 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.Enigma2/Serializer.cs b/source/ChanSort.Loader.Enigma2/Serializer.cs new file mode 100644 index 0000000..2d1d504 --- /dev/null +++ b/source/ChanSort.Loader.Enigma2/Serializer.cs @@ -0,0 +1,369 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using ChanSort.Api; + +namespace ChanSort.Loader.Enigma2 +{ + /* + * This class loads "userbouquet.*" channel lists from Enigma2 Linux set-top-boxes (Dreambox, Vu+, ...) + * + * lamedb version 4 format: https://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained + * userbouqet.* format: https://www.opena.tv/english-section/43964-iptv-service-4097-service-1-syntax.html#post376271 + */ + internal class Serializer : SerializerBase + { + private static readonly Encoding utf8WithoutBom = new UTF8Encoding(false); + + private ChannelList tv = new ChannelList(SignalSource.Digital | SignalSource.Tv, "TV"); + private ChannelList radio = new ChannelList(SignalSource.Digital | SignalSource.Radio, "Radio"); + + private readonly List favListFileNames = new(); + private readonly Dictionary transponderByLamedbId = new(); + private readonly Dictionary channelsByBouquetId = new(); + private DvbStringDecoder decoder; + + private readonly StringBuilder log = new(); + + #region ctor() + + public Serializer(string inputFile) : base(inputFile) + { + this.FileName = Path.Combine(Path.GetDirectoryName(inputFile), "lamedb"); + + this.Features.ChannelNameEdit = ChannelNameEditMode.None; + this.Features.DeleteMode = DeleteMode.Physically; + this.Features.CanSkipChannels = false; + this.Features.CanLockChannels = false; + this.Features.CanHideChannels = false; + this.Features.MixedSourceFavorites = true; + this.Features.SortedFavorites = true; + this.Features.SupportedFavorites = 0; // dynamically added + this.Features.CanSaveAs = false; + + this.tv.IsMixedSourceFavoritesList = true; + this.DataRoot.AddChannelList(this.tv); + this.radio.IsMixedSourceFavoritesList = true; + this.DataRoot.AddChannelList(this.radio); + + // hide columns for fields that don't exist in Silva-Schneider channel list + foreach (var list in this.DataRoot.ChannelLists) + { + list.VisibleColumnFieldNames.Remove("PcrPid"); + list.VisibleColumnFieldNames.Remove("VideoPid"); + list.VisibleColumnFieldNames.Remove("AudioPid"); + list.VisibleColumnFieldNames.Remove("Lock"); + list.VisibleColumnFieldNames.Remove("Skip"); + list.VisibleColumnFieldNames.Remove("Hidden"); + list.VisibleColumnFieldNames.Remove("Encrypted"); + list.VisibleColumnFieldNames.Remove("Favorites"); + list.VisibleColumnFieldNames.Remove("ServiceType"); + list.VisibleColumnFieldNames.Add("ServiceTypeName"); + } + } + + #endregion + + #region Load() + + public override void Load() + { + this.decoder = new DvbStringDecoder(this.DefaultEncoding); + this.LoadLamedb(); + + int favId = 0; + foreach(var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName), "userbouquet.*")) + this.LoadBouquet(file, ref favId); + } + + #endregion + + #region LoadLamedb() + private void LoadLamedb() + { + var path = Path.Combine(Path.GetDirectoryName(this.FileName), "lamedb"); + if (!File.Exists(path)) + throw new FileLoadException($"Could not find required file \"{path}\""); + + using var r = new StreamReader(File.OpenRead(path), utf8WithoutBom); + var line = r.ReadLine(); + if (line != "eDVB services /4/") + throw new FileLoadException($"lamedb version 4 is required"); + + string mode = null; + Transponder tp = null; + int tpId = 0, chanId = 0; + + while ((line = r.ReadLine()) != null) + { + if (line.Trim() == "") + continue; + if (line == "transponders") + mode = line; + else if (line == "services") + mode = "services"; + else if (line == "end") + mode = null; + else if (mode == "transponders") + tp = ReadLamedbTransponderLine(line, tp, ref tpId); + else if (mode == "services") + ReadLamedbServiceLine(line, r, ref chanId); + } + } + #endregion + + #region ReadLamedbTransponderLine + private Transponder ReadLamedbTransponderLine(string line, Transponder tp, ref int tpId) + { + if (line == "/") + return tp; + if (!line.StartsWith("\t")) + { + tp = new Transponder(++tpId); + this.transponderByLamedbId[line] = tp; + var parts = line.Split(':'); + tp.Number = FromHex(parts[0]); + tp.TransportStreamId = FromHex(parts[1]); + tp.OriginalNetworkId = FromHex(parts[2]); + } + else + { + if (line[1] == 's') + { + var parts = line.Substring(3).Split(':'); + tp.FrequencyInMhz = int.Parse(parts[0]) / 1000; + tp.SymbolRate = int.Parse(parts[1]) / 1000; + tp.Polarity = "HVLR"[int.Parse(parts[2])]; + tp.Satellite = GetOrCreateSatellite(int.Parse(parts[4])); + } + } + + return tp; + } + #endregion + + #region GetOrCreateSatellite + private Satellite GetOrCreateSatellite(int orbitalPos) + { + if (this.DataRoot.Satellites.TryGetValue(orbitalPos, out var sat)) + return sat; + sat = new Satellite(orbitalPos); + sat.OrbitalPosition = $"{(float) Math.Abs(orbitalPos) / 10:0.0}{(orbitalPos<0?'W':'E')}"; + sat.Name = sat.OrbitalPosition; + this.DataRoot.Satellites.Add(orbitalPos, sat); + return sat; + } + #endregion + + #region ReadLamedbServiceLine + private void ReadLamedbServiceLine(string line, StreamReader r, ref int chanId) + { + var line2 = r.ReadLine(); + var line3 = r.ReadLine(); + if (line2 == null || line3 == null) + return; + + var ch = new Channel(); + ch.SignalSource = SignalSource.Digital; + + // line 1: SID:DvbNamespace:TSID:ONID:ServiceType:ServiceNumber + var parts = line.Split(':'); + ch.RecordIndex = chanId; + ch.RecordOrder = chanId; + ch.OldProgramNr = ++chanId; + ch.NewProgramNr = chanId; + ch.IsDeleted = false; + ch.ServiceId = FromHex(parts[0]); + ch.DvbNamespace = FromHex(parts[1]); + ch.TransportStreamId = FromHex(parts[2]); + ch.OriginalNetworkId = FromHex(parts[3]); + ch.ServiceType = int.Parse(parts[4]); + ch.ServiceNumber = int.Parse(parts[5]); + ch.SignalSource |= LookupData.Instance.IsRadioTvOrData(ch.ServiceType); + + var tpId = parts[1] + ":" + parts[2] + ":" + parts[3]; + if (this.transponderByLamedbId.TryGetValue(tpId, out var tp)) + { + ch.Satellite = tp.Satellite?.Name; + ch.SymbolRate = tp.SymbolRate; + ch.FreqInMhz = tp.FrequencyInMhz; + ch.Polarity = tp.Polarity; + } + + // line 2: channel name (in raw DVB encoding) + var rawName = new byte[line2.Length]; + for (int i = 0, c = rawName.Length; i < c; i++) + rawName[i] = (byte)line2[i]; + this.decoder.GetChannelNames(rawName, 0, rawName.Length, out var longName, out var shortName); + ch.Name = longName; + ch.ShortName = shortName; + + // line 3: provider and other info + parts = line3.Split(','); + foreach (var part in parts) + { + var keyVal = part.Split(new char[] {':'}, 2); + switch (keyVal[0]) + { + case "p": + ch.Provider = keyVal[1]; + break; + } + } + + this.DataRoot.AddChannel(this.tv, ch); + this.channelsByBouquetId[$"{ch.DvbNamespace}:{ch.OriginalNetworkId}:{ch.TransportStreamId}:{ch.ServiceId}"] = ch; + } + #endregion + + #region LoadBoquet + private void LoadBouquet(string file, ref int favIndex) + { + ChannelList list; + if (file.EndsWith(".tv")) + list = this.tv; + else if (file.EndsWith(".radio")) + list = this.radio; + else + return; + + using var r = new StreamReader(File.OpenRead(file), utf8WithoutBom); + var line = r.ReadLine(); + if (line == null || !line.StartsWith("#NAME ")) + { + log.AppendLine($"{file} does not start with #NAME"); + return; + } + + this.DataRoot.SetFavListCaption(favIndex, line.Substring(6)); + this.Features.SupportedFavorites = (Favorites)((int)this.Features.SupportedFavorites<<1)|Favorites.A; + + int lineNr = 0; + int progNr = 0; + Channel ch = null; + while ((line = r.ReadLine()) != null) + { + ++lineNr; + if (line.Trim() == "") + continue; + if (line.Contains(":FROM") && line.Contains("ORDER BY")) // ignore the root-level bouquet that only references other bouquet files + return; + if (line.StartsWith("#DESCRIPTION ")) + { + if (ch != null) + ch.Description = line.Substring(13); + continue; + } + if (!line.StartsWith("#SERVICE ")) + continue; + + var parts = line.Substring(9).Split(':'); + if (parts[0] != "1") // ignore non-DVB + continue; + if (parts[1] != "0") // ignore special-purpose rows + continue; + + var prefix = parts[0] + ":" + parts[1]; + + // parts[2] = DVB service type + var sid = FromHex(parts[3]); + var tsid = FromHex(parts[4]); + var onid = FromHex(parts[5]); + var dvbNamespace = FromHex(parts[6]); + + var suffix = ""; + for (int i = 7; i < parts.Length; i++) + suffix += ":" + parts[i]; + + var key = $"{dvbNamespace}:{onid}:{tsid}:{sid}"; + if (!this.channelsByBouquetId.TryGetValue(key, out ch)) + { + log.AppendLine($"{file} line {lineNr}: service not found in lamedb"); + continue; + } + + ch.Prefix = prefix; + ch.Suffix = suffix; + ch.SetOldPosition(1+favIndex, ++progNr); + } + + this.favListFileNames.Add(file); + ++favIndex; + } + #endregion + + #region FromHex() + private int FromHex(string str) + { + int result = 0; + foreach (var ch in str) + { + if (Char.IsWhiteSpace(ch)) + continue; + + result <<= 4; + if (ch >= '0' && ch <= '9') + result += ch - '0'; + else if (ch >= 'A' && ch <= 'F') + result += ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + result += ch - 'a' + 10; + else + throw new ArgumentException(str + " contains invalid hex characters"); + } + + return result; + } + #endregion + + #region GetDataFilePaths() + public override IEnumerable GetDataFilePaths() + { + var list = new List(this.favListFileNames.Count + 1); + list.Add(this.FileName); // lamedb + list.AddRange(this.favListFileNames); // userbouquet* + return list; + } + #endregion + + + #region Save() + + public override void Save(string tvOutputFile) + { + for (int favIndex = 0; favIndex < this.favListFileNames.Count; favIndex++) + { + var file = this.favListFileNames[favIndex]; + using var w = new StreamWriter(File.OpenWrite(file), utf8WithoutBom); + w.WriteLine($"#NAME {this.DataRoot.GetFavListCaption(favIndex)}"); + foreach (var ch in this.tv.Channels.OrderBy(c => c.GetPosition(favIndex+1))) + { + if (!(ch is Channel c) || c.GetPosition(favIndex + 1) < 0) + continue; + + w.WriteLine($"#SERVICE {c.Prefix}:{c.ServiceType:X}:{c.ServiceId:X}:{c.TransportStreamId:X}:{c.OriginalNetworkId:X}:{c.DvbNamespace:X}{c.Suffix}"); + if (c.Description != null) + w.WriteLine($"#DESCRIPTION {c.Description}"); + } + } + } + + #endregion + + + #region GetFileInformation() + + public override string GetFileInformation() + { + var sb = new StringBuilder(); + sb.Append(base.GetFileInformation()); + sb.AppendLine(); + sb.Append(this.log); + return sb.ToString(); + } + + #endregion + } +} \ No newline at end of file diff --git a/source/ChanSort.Loader.Philips/BinarySerializer.cs b/source/ChanSort.Loader.Philips/BinarySerializer.cs index 5aa5811..90c9cbe 100644 --- a/source/ChanSort.Loader.Philips/BinarySerializer.cs +++ b/source/ChanSort.Loader.Philips/BinarySerializer.cs @@ -245,7 +245,7 @@ namespace ChanSort.Loader.Philips ch.Lock = mapping.GetByte("offLocked") != 0; ch.Favorites = mapping.GetByte("offIsFav") != 0 ? Favorites.A : 0; if (ch.Favorites != 0) - ch.OldFavIndex[0] = ch.OldProgramNr; + ch.SetOldPosition(1, ch.OldProgramNr); this.DataRoot.AddChannel(list, ch); } diff --git a/source/ChanSort.Loader.Philips/XmlSerializer.cs b/source/ChanSort.Loader.Philips/XmlSerializer.cs index 0317c9b..1ec88be 100644 --- a/source/ChanSort.Loader.Philips/XmlSerializer.cs +++ b/source/ChanSort.Loader.Philips/XmlSerializer.cs @@ -360,7 +360,7 @@ namespace ChanSort.Loader.Philips chan.Lock = data.TryGet("ChannelLock") == "1"; chan.Hidden = data.TryGet("UserHidden") == "1"; var fav = ParseInt(data.TryGet("FavoriteNumber")); - chan.OldFavIndex[0] = fav == 0 ? -1 : fav; + chan.SetOldPosition(1, fav == 0 ? -1 : fav); chan.OriginalNetworkId = ParseInt(data.TryGet("Onid")); chan.TransportStreamId = ParseInt(data.TryGet("Tsid")); chan.ServiceId = ParseInt(data.TryGet("Sid")); diff --git a/source/ChanSort.Loader.Samsung/Scm/ScmChannelBase.cs b/source/ChanSort.Loader.Samsung/Scm/ScmChannelBase.cs index cedf362..cee85e7 100644 --- a/source/ChanSort.Loader.Samsung/Scm/ScmChannelBase.cs +++ b/source/ChanSort.Loader.Samsung/Scm/ScmChannelBase.cs @@ -94,7 +94,7 @@ namespace ChanSort.Loader.Samsung.Scm else if (sortedFavorites != FavoritesIndexMode.Boolean && favValue != -1) fav |= mask; if (sortedFavorites == FavoritesIndexMode.IndividuallySorted) - this.OldFavIndex[favIndex] = favValue; + this.SetOldPosition(1+favIndex, favValue); mask <<= 1; ++favIndex; } diff --git a/source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs b/source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs index 9633541..fd433f7 100644 --- a/source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs +++ b/source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs @@ -616,8 +616,8 @@ namespace ChanSort.Loader.Samsung.Zip // update favorites for (int i=0, mask=1; i<5; i++, mask <<= 1) { - int oldPos = channel.OldFavIndex[i]; - int newPos = ((int)channel.Favorites & mask) != 0 ? channel.FavIndex[i] : -1; + int oldPos = channel.GetOldPosition(1+i); + int newPos = ((int)channel.Favorites & mask) != 0 ? channel.GetPosition(1+i) : -1; if (newPos >= 0) { diff --git a/source/ChanSort.sln b/source/ChanSort.sln index 0e184c6..c835533 100644 --- a/source/ChanSort.sln +++ b/source/ChanSort.sln @@ -69,6 +69,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Grundig", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.M3u", "Test.Loader.M3u\Test.Loader.M3u.csproj", "{052692BF-D782-4888-B34D-89D6B1379340}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Enigma2", "ChanSort.Loader.Enigma2\ChanSort.Loader.Enigma2.csproj", "{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -379,6 +381,18 @@ Global {052692BF-D782-4888-B34D-89D6B1379340}.Release|Mixed Platforms.Build.0 = Release|Any CPU {052692BF-D782-4888-B34D-89D6B1379340}.Release|x86.ActiveCfg = Release|Any CPU {052692BF-D782-4888-B34D-89D6B1379340}.Release|x86.Build.0 = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|x86.ActiveCfg = Debug|x86 + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|x86.Build.0 = Debug|x86 + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Any CPU.Build.0 = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|x86.ActiveCfg = Release|Any CPU + {4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.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 d068f7f..2d1b4f5 100644 --- a/source/ChanSort/ChanSort.csproj +++ b/source/ChanSort/ChanSort.csproj @@ -482,6 +482,14 @@ {DCCFFA08-472B-4D17-BB90-8F513FC01392} ChanSort.Api + + {4ad7f77e-617c-4741-82ae-e7a41c85ee4d} + ChanSort.Loader.Enigma2 + + + {4d5af0a3-1b96-42c8-910d-0c4852ea22f4} + ChanSort.Loader.Grundig + {d093e7ee-d3ad-4e7b-af82-c6918ca017fb} ChanSort.Loader.Hisense @@ -506,6 +514,10 @@ {a1c9a98d-368a-44e8-9b7f-7eaca46c9ec5} ChanSort.Loader.Samsung + + {e6279ff8-362a-41e6-ac0d-d0861d43f01c} + ChanSort.Loader.SatcoDX + {70e29c6b-b926-4859-9548-23375bf1e1b5} ChanSort.Loader.Sony diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index df2732a..2144fbc 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -244,8 +244,8 @@ namespace ChanSort.Ui extension.Append(ext); extension.Append(";"); } - ++numberOfFilters; } + ++numberOfFilters; } if (extension.Length > 0) extension.Remove(extension.Length - 1, 1); diff --git a/source/ChanSort/ReferenceListForm.cs b/source/ChanSort/ReferenceListForm.cs index 0f6f0a0..7158ec2 100644 --- a/source/ChanSort/ReferenceListForm.cs +++ b/source/ChanSort/ReferenceListForm.cs @@ -18,6 +18,18 @@ namespace ChanSort.Ui private SerializerBase serializer; private readonly string[] closeButtonText; + class MixedSourceList + { + public ChannelList ChannelList { get; } + public int FavIndex { get; } + + public MixedSourceList(ChannelList list, int favIndex) + { + ChannelList = list; + FavIndex = favIndex; + } + } + public ReferenceListForm(MainForm main) { this.main = main; @@ -113,7 +125,13 @@ namespace ChanSort.Ui this.comboSource.Properties.Items.Clear(); foreach (var list in serializer.DataRoot.ChannelLists) { - if (!list.IsMixedSourceFavoritesList && list.Channels.Count > 0) + if (list.Channels.Count == 0) + continue; + if (list.IsMixedSourceFavoritesList) + { + this.comboSource.Properties.Items.Add(new MixedSourceList(list, i)) + } + else this.comboSource.Properties.Items.Add(list); }