diff --git a/source/ChanSort.Api/Model/Transponder.cs b/source/ChanSort.Api/Model/Transponder.cs
index ad47bae..94b3c3b 100644
--- a/source/ChanSort.Api/Model/Transponder.cs
+++ b/source/ChanSort.Api/Model/Transponder.cs
@@ -12,6 +12,7 @@
public char Polarity { get; set; }
public int OriginalNetworkId { get; set; }
public int TransportStreamId { get; set; }
+ public SignalSource SignalSource { get; set; }
public Transponder(int id)
{
diff --git a/source/ChanSort.Loader.Hisense/ServicelistDb/ServicelistDbSerializer.cs b/source/ChanSort.Loader.Hisense/ServicelistDb/ServicelistDbSerializer.cs
index 35832e7..b21fe1f 100644
--- a/source/ChanSort.Loader.Hisense/ServicelistDb/ServicelistDbSerializer.cs
+++ b/source/ChanSort.Loader.Hisense/ServicelistDb/ServicelistDbSerializer.cs
@@ -623,7 +623,6 @@ left outer join {dbSchema.DvbServiceTable} digs on digs.ServiceId=s.Pid
{
}
- public SignalSource SignalSource { get; set; }
public string Source { get; set; }
}
diff --git a/source/ChanSort.Loader.Loewe/ChanSort.Loader.Loewe.csproj b/source/ChanSort.Loader.Loewe/ChanSort.Loader.Loewe.csproj
new file mode 100644
index 0000000..b004757
--- /dev/null
+++ b/source/ChanSort.Loader.Loewe/ChanSort.Loader.Loewe.csproj
@@ -0,0 +1,59 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}
+ Library
+ Properties
+ ChanSort.Loader.Loewe
+ ChanSort.Loader.Loewe
+ v4.8
+ 512
+ true
+
+
+
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ latest
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ latest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {dccffa08-472b-4d17-bb90-8f513fc01392}
+ ChanSort.Api
+
+
+
+
\ No newline at end of file
diff --git a/source/ChanSort.Loader.Loewe/Channel.cs b/source/ChanSort.Loader.Loewe/Channel.cs
new file mode 100644
index 0000000..5f653fa
--- /dev/null
+++ b/source/ChanSort.Loader.Loewe/Channel.cs
@@ -0,0 +1,15 @@
+using System.Xml;
+using ChanSort.Api;
+
+namespace ChanSort.Loader.Loewe
+{
+ internal class Channel : ChannelInfo
+ {
+ public XmlElement XmlElement { get; set; }
+ public int PhysicalListId { get; set; }
+
+ public Channel(int id) : base(0, id, -1, "")
+ {
+ }
+ }
+}
diff --git a/source/ChanSort.Loader.Loewe/LoewePlugin.cs b/source/ChanSort.Loader.Loewe/LoewePlugin.cs
new file mode 100644
index 0000000..edee8ee
--- /dev/null
+++ b/source/ChanSort.Loader.Loewe/LoewePlugin.cs
@@ -0,0 +1,18 @@
+using ChanSort.Api;
+
+namespace ChanSort.Loader.Loewe
+{
+ // The servicelist.db files are handled by the Hisense loader, which shares the same file format
+
+ public class LoewePlugin : ISerializerPlugin
+ {
+ public string DllName { get; set; }
+ public string PluginName => "Loewe (servicelist.xml)";
+ public string FileFilter => "*.xml";
+
+ public SerializerBase CreateSerializer(string inputFile)
+ {
+ return new Serializer(inputFile);
+ }
+ }
+}
diff --git a/source/ChanSort.Loader.Loewe/Properties/AssemblyInfo.cs b/source/ChanSort.Loader.Loewe/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..820d02a
--- /dev/null
+++ b/source/ChanSort.Loader.Loewe/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.Loewe")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ChanSort.Loader.Loewe")]
+[assembly: AssemblyCopyright("Copyright © 2022")]
+[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("d4b9399d-5609-4f87-a4ba-5b35983a981b")]
+
+// 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.Loewe/Serializer.cs b/source/ChanSort.Loader.Loewe/Serializer.cs
new file mode 100644
index 0000000..ef6c677
--- /dev/null
+++ b/source/ChanSort.Loader.Loewe/Serializer.cs
@@ -0,0 +1,465 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using ChanSort.Api;
+
+namespace ChanSort.Loader.Loewe;
+
+/*
+ * The XML used in by this loader seems like a transformed version of the SQLite3 database format used by the Hisense/Loewe loader
+ *
+ *
+ * The 2017 Hisense / Loewe data model for channel lists is a bit different than all other supported models and need some workarounds to be supported.
+ * It is based on a flat "Services" table which doesn't hold program numbers and a FavoritesList/FavoritesItem table to assign numbers
+ * to physical tuner lists and user favorite lists alike.
+ *
+ * Physical channel lists (e.g. for $av, Astra, Hot Bird) have their own ChannelList in the channelList dictionary and use
+ * ChannelInfo.NewProgramNr to hold the program number. This doesn't allow the user to add services from other lists.
+ *
+ * The user favorite lists (FAV1-FAV4) use the separate favList ChannelList filled with all services from all physical lists.
+ * ChannelInfo.FavIndex[0-3] holds the information for the program numbers in FAV1-4. The value -1 is used to indicate "not included".
+ *
+ * The $all list is hidden from the user and automatically updated to match the contents of all other lists (except $av and FAV1-4).
+ *
+ * The $av list is hidden from the user and not updated at all.
+ *
+ * This loader poses the following restrictions on the database:
+ * - a service must not appear in more than one physical channel list ($all and FAV1-4 are not part of this restriction)
+ * - a service can't appear more than once in any list
+ *
+ */
+
+class Serializer : SerializerBase
+{
+ private XmlDocument doc;
+ private readonly StringBuilder fileInfo = new();
+
+ private readonly ChannelList mixedFavTv;
+ private readonly ChannelList mixedFavRadio;
+
+ ///
+ /// Fields of the ChannelInfo that will be shown in the UI
+ ///
+ private static readonly List ColumnNames = new()
+ {
+ "OldPosition",
+ "Position",
+ "Source",
+ "NewProgramNr",
+ "Name",
+ "ShortName",
+ "Favorites",
+ "Skip",
+ "Lock",
+ "Hidden",
+ "Encrypted",
+ "FreqInMhz",
+ "OriginalNetworkId",
+ "TransportStreamId",
+ "ServiceId",
+ //"ServiceType",
+ "ServiceTypeName",
+ "NetworkName",
+ "Satellite"
+ // "SymbolRate"
+ };
+
+ ///
+ /// mapping of FavoriteList.Pid => ChannelList.
+ /// This dict does not include real user favorite lists (FAV1-FAV4).
+ ///
+ private readonly Dictionary channelLists = new();
+
+ ///
+ /// mapping of FavoriteList.Pid for $all and FAV1-4 => index of the internal favorite list within userFavList (0-3)
+ /// Pids that don't belong to the FAV1-4 are not included in this dictionary.
+ ///
+ private readonly Dictionary favListIdToFavIndex = new();
+
+ ///
+ /// mapping of Service.Pid => ChannelInfo
+ ///
+ private readonly Dictionary channelsById = new();
+
+ ///
+ /// FavoriteList.Pid of the $all list
+ ///
+ private int pidAll;
+
+ ///
+ /// FavoriteList.Pid of the $av list
+ ///
+ private int pidAv;
+
+
+
+ #region ctor()
+
+ public Serializer(string inputFile) : base(inputFile)
+ {
+ Features.ChannelNameEdit = ChannelNameEditMode.All;
+ Features.DeleteMode = DeleteMode.NotSupported;
+ Features.CanSkipChannels = true;
+ Features.CanLockChannels = true;
+ Features.CanHideChannels = true;
+ Features.CanHaveGaps = true;
+ Features.FavoritesMode = FavoritesMode.MixedSource;
+
+ this.mixedFavTv = new ChannelList(0, "TV");
+ this.mixedFavTv.VisibleColumnFieldNames = ColumnNames;
+ this.mixedFavTv.IsMixedSourceFavoritesList = true;
+
+ this.mixedFavRadio = new ChannelList(0, "Radio");
+ this.mixedFavRadio.VisibleColumnFieldNames = ColumnNames;
+ this.mixedFavRadio.IsMixedSourceFavoritesList = true;
+ }
+
+ #endregion
+
+ #region Load()
+
+ public override void Load()
+ {
+ this.doc = new XmlDocument();
+ doc.Load(this.FileName);
+ var sl = doc["servicelist"];
+ if (sl == null)
+ throw new FileLoadException("expected root element ");
+
+ var tuners = sl["tuners"];
+ if (tuners == null)
+ throw new FileLoadException("missing list");
+
+ var services = sl["services"];
+ if (services == null)
+ throw new FileLoadException("missing list");
+
+ var favorites = sl["favorites"];
+ if (favorites == null)
+ throw new FileLoadException("missing list");
+
+ LoadTuners(tuners);
+ LoadServices(services);
+ LoadFavorites(favorites);
+
+ Features.MaxFavoriteLists = this.favListIdToFavIndex.Count;
+ this.channelLists.Add(0, mixedFavTv);
+ this.channelLists.Add(0x8000, mixedFavRadio);
+ }
+ #endregion
+
+ #region LoadTuners()
+
+ private void LoadTuners(XmlElement tuners)
+ {
+ int index = 0;
+ foreach (var child in tuners.ChildNodes)
+ {
+ if (child is not XmlElement e)
+ continue;
+ if (e.LocalName == "tuner")
+ LoadTunerBaseData(e, index++);
+ else if (e.LocalName == "dvbt2-tuner")
+ LoadDvbTunerData(e);
+ }
+ }
+
+ private void LoadTunerBaseData(XmlElement e, int index)
+ {
+ var a = e.Attributes;
+ var id = int.Parse(a["TunerId"].InnerText);
+ var t = new Transponder(id);
+ t.OriginalNetworkId = int.Parse(a["Oid"].InnerText);
+ t.TransportStreamId = int.Parse(a["Tid"].InnerText);
+
+ if (e.LocalName == "dvbt-tuner" || e.LocalName == "dvbt2-tuner")
+ t.SignalSource |= SignalSource.Antenna;
+ else if (e.LocalName == "dvbc-tuner" || e.LocalName == "dvbc2-tuner")
+ t.SignalSource |= SignalSource.Cable;
+ else if (e.LocalName == "dvbs-tuner" || e.LocalName == "dvbs2-tuner")
+ t.SignalSource |= SignalSource.Sat;
+
+ DataRoot.Transponder.Add(id, t);
+ }
+
+ private void LoadDvbTunerData(XmlElement e)
+ {
+ var a = e.Attributes;
+ var id = int.Parse(a["TunerId"].InnerText);
+ if (!DataRoot.Transponder.TryGetValue(id, out var t))
+ return;
+ t.FrequencyInMhz = decimal.Parse(a["Frequency"].InnerText) / 1000;
+ }
+
+ #endregion
+
+ #region LoadServices
+
+ private void LoadServices(XmlElement services)
+ {
+ int index = 0;
+ foreach (var child in services.ChildNodes)
+ {
+ if (child is not XmlElement e)
+ continue;
+ if (e.LocalName == "service")
+ LoadServiceBaseData(e, index++);
+ else if (e.LocalName == "analog-service")
+ LoadServiceAnalogData(e);
+ else if (e.LocalName == "dvb-service")
+ LoadServiceDigitalData(e);
+ }
+ }
+
+ private void LoadServiceBaseData(XmlElement e, int index)
+ {
+ var a = e.Attributes;
+ var id = int.Parse(a["Pid"].InnerText);
+ var c = new Channel(id);
+ c.XmlElement = e;
+ this.channelsById[id] = c;
+ c.RecordOrder = index;
+ c.Name = a["Name"].InnerText;
+ c.Lock = a["ParentalLock"].InnerText == "1";
+ c.Skip = a["Selectable"].InnerText == "0";
+ c.ShortName = a["ShortName"].InnerText;
+ c.Hidden = a["Visible"].InnerText == "0";
+
+ ChannelList list;
+ switch (int.Parse(a["MediaType"].InnerText))
+ {
+ case 1: c.SignalSource |= SignalSource.Tv;
+ list = mixedFavTv;
+ break;
+ case 2: c.SignalSource |= SignalSource.Radio;
+ list = mixedFavRadio;
+ break;
+ default:
+ list = mixedFavTv;
+ break;
+ }
+
+ list.AddChannel(c);
+ }
+
+ private void LoadServiceAnalogData(XmlElement e)
+ {
+ var a = e.Attributes;
+ var id = int.Parse(a["ServiceId"].InnerText);
+ if (!this.channelsById.TryGetValue(id, out var c))
+ return;
+ c.FreqInMhz = decimal.Parse(a["Frequency"].InnerText) / 1000;
+ c.SignalSource |= SignalSource.Analog;
+ }
+
+ private void LoadServiceDigitalData(XmlElement e)
+ {
+ var a = e.Attributes;
+ var id = int.Parse(a["ServiceId"].InnerText);
+ if (!this.channelsById.TryGetValue(id, out var c))
+ return;
+ c.ServiceId = int.Parse(a["Sid"].InnerText);
+ c.SignalSource |= SignalSource.Digital;
+
+ var transponderId = int.Parse(a["TunerId"].InnerText);
+ var t = DataRoot.Transponder.TryGet(transponderId);
+ if (t != null)
+ {
+ c.Transponder = t;
+ c.OriginalNetworkId = t.OriginalNetworkId;
+ c.TransportStreamId = t.TransportStreamId;
+ c.FreqInMhz = t.FrequencyInMhz;
+ c.SignalSource |= t.SignalSource;
+ }
+ }
+ #endregion
+
+ #region LoadFavorites()
+
+ private void LoadFavorites(XmlElement favorites)
+ {
+ foreach (var node in favorites.ChildNodes)
+ {
+ if (node is not XmlElement e)
+ continue;
+ if (e.LocalName == "favorite-list")
+ LoadFavoriteList(e);
+ else if (e.LocalName == "favorite-item")
+ LoadFavoriteItem(e);
+ else if (e.LocalName == "lcn")
+ LoadLcnItem(e);
+ }
+
+ this.DataRoot.AddChannelList(this.mixedFavTv);
+ this.DataRoot.AddChannelList(this.mixedFavRadio);
+ }
+
+ private void LoadFavoriteList(XmlElement e)
+ {
+ var a = e.Attributes;
+ var name = a["Name"].InnerText;
+ var creator = a["Creator"].InnerText;
+ var id = int.Parse(a["Pid"].InnerText);
+
+ if (name == "$av")
+ this.pidAv = id;
+ else if (creator.StartsWith("User.") || name == "$all")
+ {
+ if (name == "$all")
+ this.pidAll = id;
+
+ var idx = this.favListIdToFavIndex.Count;
+ this.favListIdToFavIndex[id] = idx + 1;
+ this.mixedFavTv.SetFavListCaption(idx, name);
+ this.mixedFavRadio.SetFavListCaption(idx, name);
+ return;
+ }
+
+ var list = new ChannelList(0, name + " TV");
+ list.VisibleColumnFieldNames = ColumnNames;
+ channelLists.Add(id, list);
+ DataRoot.AddChannelList(list);
+ if (name.StartsWith("$av"))
+ {
+ list.ShortCaption = "A/V";
+ list.ReadOnly = true;
+ return;
+ }
+
+ list = new ChannelList(0, name + " Radio");
+ list.VisibleColumnFieldNames = ColumnNames;
+ channelLists.Add(id | 0x8000, list);
+ DataRoot.AddChannelList(list);
+ }
+
+ private void LoadFavoriteItem(XmlElement e)
+ {
+ var a = e.Attributes;
+ var listId = int.Parse(a["FavoriteId"].InnerText);
+ var serviceId = int.Parse(a["ServiceId"].InnerText);
+ var channelNo = int.Parse(a["ChannelNum"].InnerText);
+
+ if (!this.channelsById.TryGetValue(serviceId, out var c))
+ return;
+
+ if (this.favListIdToFavIndex.TryGetValue(listId, out var listIdx))
+ c.SetOldPosition(listIdx, channelNo);
+ else
+ {
+ c.OldProgramNr = channelNo;
+ ((Channel)c).PhysicalListId = listId;
+ }
+
+ if (listIdx == 0)
+ {
+ if ((c.SignalSource & SignalSource.Radio) != 0)
+ listId |= 0x8000;
+ var list = channelLists.TryGet(listId);
+ DataRoot.AddChannel(list, c);
+ }
+ }
+
+ private void LoadLcnItem(XmlElement e)
+ {
+ var a = e.Attributes;
+ var listId = int.Parse(a["FavoriteId"].InnerText);
+ var serviceId = int.Parse(a["ServiceId"].InnerText);
+ var lcn = int.Parse(a["Lcn"].InnerText);
+
+ if (!this.channelsById.TryGetValue(serviceId, out var c))
+ return;
+
+ c.ProgramNrPreset = lcn;
+ if (this.favListIdToFavIndex.TryGetValue(listId, out var listIdx))
+ {
+ var list = channelLists.TryGet(listId);
+ list.ReadOnly = true;
+ }
+ else if (this.channelLists.TryGetValue(listId, out var list))
+ {
+ list.ReadOnly = true;
+ }
+ }
+
+ #endregion
+
+ // Save
+
+ #region Save()
+
+ public override void Save(string tvOutputFile)
+ {
+ var fav = this.doc["servicelist"]["favorites"];
+ var elements = fav.GetElementsByTagName("favorite-item");
+ var items = new List();
+ foreach(XmlNode node in elements)
+ items.Add(node);
+ foreach (var node in items)
+ node.ParentNode.RemoveChild(node);
+
+ elements = fav.GetElementsByTagName("lcn");
+ var lcn = new List();
+ foreach (XmlNode node in elements)
+ lcn.Add(node);
+ foreach (var node in lcn)
+ node.ParentNode.RemoveChild(node);
+
+ var idVal = 0;
+ var lists = new List>();
+ foreach (var list in this.DataRoot.ChannelLists)
+ {
+ if (list.IsMixedSourceFavoritesList)
+ {
+ foreach (var entry in favListIdToFavIndex)
+ lists.Add(Tuple.Create(entry.Key, list, entry.Value));
+ }
+ else
+ {
+ lists.Add(Tuple.Create(channelLists.FirstOrDefault(e => e.Value == list).Key, list, 0));
+ }
+ }
+
+ //var listIds = this.channelLists.Keys.Union(this.favListIdToFavIndex.Keys).OrderBy(k => k).ToList();
+ foreach (var tuple in lists)
+ {
+ var listId = tuple.Item1 & 0x7FFF;
+ var list = tuple.Item2;
+ var favIndex = tuple.Item3;
+ foreach (var chan in list.Channels)
+ {
+ if (chan is not Channel c || c.IsProxy)
+ continue;
+
+ var chno = c.GetPosition(favIndex);
+ if (chno < 0)
+ continue;
+
+ var e = doc.CreateElement("favorite-item");
+ e.SetAttribute("Active", "0");
+ e.SetAttribute("Attribute", "0");
+ e.SetAttribute("ChannelNum", chno.ToString());
+ e.SetAttribute("FavoriteId", listId.ToString());
+ e.SetAttribute("Id", (++idVal).ToString());
+ e.SetAttribute("OriginalFavoriteId", listId.ToString());
+ e.SetAttribute("Selectable", "-1");
+ e.SetAttribute("ServiceId", c.RecordIndex.ToString());
+ e.SetAttribute("ServiceName", "");
+ e.SetAttribute("Visible", "-1");
+ fav.AppendChild(e);
+ }
+ }
+
+ foreach (XmlNode item in lcn)
+ fav.AppendChild(item);
+
+ doc.Save(tvOutputFile);
+ this.FileName = tvOutputFile;
+ }
+
+ #endregion
+
+}
+
diff --git a/source/ChanSort.Loader.Philips/XmlSerializer.cs b/source/ChanSort.Loader.Philips/XmlSerializer.cs
index fc9d127..8926de3 100644
--- a/source/ChanSort.Loader.Philips/XmlSerializer.cs
+++ b/source/ChanSort.Loader.Philips/XmlSerializer.cs
@@ -159,7 +159,7 @@ namespace ChanSort.Loader.Philips
// ChannelMap_100/ChannelList/chanLst.bin
// + optionally
// ChannelMap_100/ChannelList/channelFile.bin
- // ChannelMap_100/ChannelList/Favorite.xml
+ // ChannelMap_105/ChannelList/Favorite.xml
// ChannelMap_100/ChannelList/satInfo.bin
var dataFiles = new[] { @"channellib\DVBC.xml", @"channellib\DVBT.xml", @"s2channellib\DVBS.xml", @"s2channellib\DVBSall.xml", @"Favorite.xml" };
diff --git a/source/ChanSort.sln b/source/ChanSort.sln
index e3eafc7..278f882 100644
--- a/source/ChanSort.sln
+++ b/source/ChanSort.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29025.244
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort", "ChanSort\ChanSort.csproj", "{5FAFDABC-A52F-498C-BD2F-AFFC4119797A}"
ProjectSection(ProjectDependencies) = postProject
@@ -94,6 +94,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.CmdbBin", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Android", "ChanSort.Loader.Android\ChanSort.Loader.Android.csproj", "{5088DB0D-6BDE-4678-8C50-A14E6A294A45}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Loewe", "ChanSort.Loader.Loewe\ChanSort.Loader.Loewe.csproj", "{D4B9399D-5609-4F87-A4BA-5B35983A981B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
All_Debug|Any CPU = All_Debug|Any CPU
@@ -1131,6 +1133,36 @@ Global
{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
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|x86.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Debug|x86.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|Any CPU.Build.0 = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|x86.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.All_Release|x86.Build.0 = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Debug|x86.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|x86.ActiveCfg = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.NoDevExpress_Debug|x86.Build.0 = Debug|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.Release|x86.ActiveCfg = Release|Any CPU
+ {D4B9399D-5609-4F87-A4BA-5B35983A981B}.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 7e4579a..c51a940 100644
--- a/source/ChanSort/ChanSort.csproj
+++ b/source/ChanSort/ChanSort.csproj
@@ -555,6 +555,10 @@
{e972d8a1-2f5f-421c-ac91-cff45e5191be}
ChanSort.Loader.LG
+
+ {d4b9399d-5609-4f87-a4ba-5b35983a981b}
+ ChanSort.Loader.Loewe
+
{484028b6-3aae-4f7e-a88a-76beeb70203b}
ChanSort.Loader.M3u
@@ -575,6 +579,10 @@
{e6279ff8-362a-41e6-ac0d-d0861d43f01c}
ChanSort.Loader.SatcoDX
+
+ {4e68f218-5135-4d92-8c17-14faa5d4cbf3}
+ ChanSort.Loader.Sharp
+
{70e29c6b-b926-4859-9548-23375bf1e1b5}
ChanSort.Loader.Sony
diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs
index 94bec8c..f2836d5 100644
--- a/source/ChanSort/MainForm.cs
+++ b/source/ChanSort/MainForm.cs
@@ -1996,7 +1996,7 @@ namespace ChanSort.Ui
private void ExportExcelList()
{
- const string header = "List;Pr#;Channel Name;Favorites;Lock;Skip;Hide;Encrypted;Satellite;Ch/Tp;Freq;ONID;TSID;SymRate;SID;VPID;APID";
+ const string header = "List;Pr#;Channel Name;Favorites;Lock;Skip;Hide;Encrypted;Satellite;Ch/Tp;Freq;Pol;SymRate;ONID;TSID;SID;VPID;APID";
const char sep = '\t';
var sb = new StringBuilder();
sb.AppendLine(header.Replace(';', sep));
@@ -2017,9 +2017,10 @@ namespace ChanSort.Ui
sb.Append('"').Append(channel.Satellite).Append('"').Append(sep);
sb.Append(channel.ChannelOrTransponder).Append(sep);
sb.Append(channel.FreqInMhz).Append(sep);
+ sb.Append(channel.Polarity).Append(sep);
+ sb.Append(channel.SymbolRate).Append(sep);
sb.Append(channel.OriginalNetworkId).Append(sep);
sb.Append(channel.TransportStreamId).Append(sep);
- sb.Append(channel.SymbolRate).Append(sep);
sb.Append(channel.ServiceId).Append(sep);
sb.Append(channel.VideoPid).Append(sep);
sb.Append(channel.AudioPid);
diff --git a/source/ChanSort/Properties/licenses.licx b/source/ChanSort/Properties/licenses.licx
index e69de29..6e5093b 100644
--- a/source/ChanSort/Properties/licenses.licx
+++ b/source/ChanSort/Properties/licenses.licx
@@ -0,0 +1,5 @@
+DevExpress.XtraEditors.ButtonEdit, DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
+DevExpress.XtraBars.BarManager, DevExpress.XtraBars.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
+DevExpress.XtraEditors.CheckEdit, DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
+DevExpress.XtraEditors.PictureEdit, DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
+DevExpress.XtraEditors.Repository.RepositoryItemTextEdit, DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
diff --git a/source/changelog.md b/source/changelog.md
index fb956b1..84af5ab 100644
--- a/source/changelog.md
+++ b/source/changelog.md
@@ -1,8 +1,13 @@
ChanSort Change Log
===================
-TBD
+2022-03-20
- Philips \*Table and \*.dat format: now showing "Encrypted" information
+- added ChangHong LED40D3000ISX dtv_cmdb_2.bin format (1489 KB size)
+- added experimental support for Loewe servicelist.xml format
+- added polarity information to Excel export (and changed column order slightly)
+- added missing Romanian translation files to the .zip
+
2021-10-24
- LG webOS 5 and 6: Improved support for DVB-C lists which changed channel numbers after import
diff --git a/source/makeDistribZip.cmd b/source/makeDistribZip.cmd
index d684bde..319344c 100644
--- a/source/makeDistribZip.cmd
+++ b/source/makeDistribZip.cmd
@@ -3,7 +3,7 @@ setlocal
setlocal enabledelayedexpansion
cd /d %~dp0
-set languages=cs de es hu pl pt ru tr
+set languages=cs de es hu pl pt ro ru tr
set curdate=%date:~6,4%-%date:~3,2%-%date:~0,2%
set target=%cd%\..\..\ChanSort_%curdate%
set DXversion=21.1