- ChanSort didn't work properly for Turkish (and other system locales) that don't translate ".bin" to uppercase

".BIN" and ".BIN" to lowercase ".bin".
- Philips: Removing a channel from a list is no longer possible (because it is not supported by the TV's import).
  When saving a file, all unsorted channels will be placed at the end of the list.
- support for Philips Repair\mgr_chan_s_fta.db lists is now read-only. Files can be used as a reference list,
  but modifying them won't be possible.
This commit is contained in:
Horst Beham
2021-08-31 22:13:28 +02:00
parent b0b09de733
commit 1b250b2a18
24 changed files with 225 additions and 148 deletions

View File

@@ -12,6 +12,9 @@ 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.
*/
class DbSerializer : SerializerBase
{
@@ -26,6 +29,7 @@ namespace ChanSort.Loader.Philips
this.Features.MaxFavoriteLists = 1;
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
this.Features.DeleteMode = DeleteMode.NotSupported;
this.Features.CanHaveGaps = false;
string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini");
this.ini = new IniFile(iniFile);
@@ -43,6 +47,7 @@ namespace ChanSort.Loader.Philips
nameof(Channel.OriginalNetworkId),
nameof(Channel.ServiceId)
};
dvbsChannels.ReadOnly = true;
}
#region Load()
@@ -163,7 +168,7 @@ namespace ChanSort.Loader.Philips
var offFooterChecksum = sec.GetInt("offFooterChecksum");
var mapping = new DataMapping(ini.GetSection("mgr_chan_s_fta.db_entry"));
#if JUST_CHANGE_NUMBERS
// update channel data
foreach (var ch in dvbsChannels.Channels)
{
@@ -172,7 +177,28 @@ namespace ChanSort.Loader.Philips
mapping.SetWord("offPrevProgNr", ch.NewProgramNr - 1);
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
}
#else
// physically reorder channels
var newData = new byte[data.Length];
Array.Copy(data, newData, lenHeader);
var off = lenHeader + lenEntry * dvbsChannels.Channels.Count;
Array.Copy(data, off, newData, off, lenFooter);
int i = 0;
foreach (var ch in dvbsChannels.Channels.OrderBy(c => c.NewProgramNr))
{
off = lenHeader + i * lenEntry;
Array.Copy(data, lenHeader + ch.RecordOrder * lenEntry, newData, off, lenEntry);
mapping.SetDataPtr(newData, off);
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetWord("offPrevProgNr", ch.NewProgramNr - 1);
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
ch.RecordOrder = i;
++i;
}
data = newData;
#endif
// update checksum
var offChecksum = data.Length - lenFooter + offFooterChecksum;
var checksum = CalcChecksum(data, 0, offChecksum);

View File

@@ -85,7 +85,7 @@ namespace ChanSort.Loader.Philips
public SerializerBase CreateSerializer(string inputFile)
{
int majorVersion = int.MinValue;
var filename = Path.GetFileName(inputFile).ToLower();
var filename = Path.GetFileName(inputFile).ToLowerInvariant();
if (Regex.IsMatch(filename, @"^CM_.*\.(?:bin|xml)$", RegexOptions.IgnoreCase))
majorVersion = 0;
else
@@ -111,7 +111,7 @@ namespace ChanSort.Loader.Philips
break;
}
var dirName = Path.GetFileName(dir).ToLower();
var dirName = Path.GetFileName(dir).ToLowerInvariant();
if (dirName == "channellib" || dirName == "s2channellib")
dir = Path.GetDirectoryName(dir);
else if (Directory.Exists(Path.Combine(dir, "PhilipsChannelMaps")))

View File

@@ -14,11 +14,19 @@ namespace ChanSort.Loader.Philips
This loader supports 2 different kinds of XML files from Philips, the first in a "Repair" folder, the others in a "ChannelMap_100" (or later) folder
Example from Repair\CM_TPM1013E_LA_CK.xml:
This "Repair" format comes with a visible .BIN file and a .xml file that has file system attributes "hidden" and "system".
The TV seems to use the .BIN file as its primary source for setting up the internal list and then applies the .xml on top of it to reorder channels.
It uses the "oldpresetnumber" from the XML to lookup the channel from the .BIN file and then apply the new "presetnumber".
The .BIN file itself is compressed with some unknown cl_Zip compression / archive format and can't be edited with ChanSort.
Deleting a channel is not possible by modifiying the .xml file. Omitting a channel only results in duplicate numbers with the TV still showing the missing channels at their old numbers.
<Channel>
<Setup oldpresetnumber="1" presetnumber="1" name="Das Erste" ></Setup>
<Broadcast medium="dvbc" frequency="410000" system="west" serviceID="1" ONID="41985" TSID="1101" modulation="256" symbolrate="6901000" bandwidth="Unknown"></Broadcast>
</Channel>
Newer channel lists from Philips contain multiple XML files with a different internal structure, which also varies based on the version number in the ChannelMap_xxx folder name:
<Channel>
<Setup SatelliteName="0x54 0x00 0x55 0x00 0x52 0x00 0x4B 0x00 0x53 0x00 0x41 0x00 0x54 0x00 0x20 0x00 0x34 0x00 0x32 0x00 0x45 0x00 " ChannelNumber="1" ChannelName="0x54 0x00 0xC4 0x00 0xB0 0x00 0x56 0x00 0xC4 0x00 0xB0 0x00 0x42 0x00 0x55 0x00 0x20 0x00 0x53 0x00 0x50 0x00 0x4F 0x00 0x52 0x00 " ChannelLock="0" UserModifiedName="0" LogoID="0" UserModifiedLogo="0" LogoLock="0" UserHidden="0" FavoriteNumber="0" />
@@ -70,7 +78,7 @@ namespace ChanSort.Loader.Philips
this.Features.CanSkipChannels = false;
this.Features.CanLockChannels = true;
this.Features.CanHideChannels = true;
this.Features.DeleteMode = DeleteMode.Physically;
this.Features.DeleteMode = DeleteMode.NotSupported;
this.Features.CanSaveAs = false;
this.Features.AllowGapsInFavNumbers = false;
this.Features.CanEditFavListNames = true;
@@ -132,7 +140,7 @@ namespace ChanSort.Loader.Philips
// support for files in a ChannelMap_xxx directory structure
bool isChannelMapFolderStructure = false;
var dir = Path.GetDirectoryName(this.FileName);
var dirName = Path.GetFileName(dir).ToLower();
var dirName = Path.GetFileName(dir).ToLowerInvariant();
if (dirName == "channellib" || dirName == "s2channellib")
{
dir = Path.GetDirectoryName(dir);
@@ -147,7 +155,7 @@ namespace ChanSort.Loader.Philips
this.chanLstBin = new ChanLstBin();
this.chanLstBin.Load(this.FileName, msg => this.logMessages.AppendLine(msg));
}
else if (Path.GetExtension(this.FileName).ToLower() == ".bin")
else if (Path.GetExtension(this.FileName).ToLowerInvariant() == ".bin")
{
// older Philips models export a visible file like Repair\CM_T911_LA_CK.BIN and an invisible (hidden+system) .xml file with the same name
var xmlPath = Path.Combine(dir, Path.GetFileNameWithoutExtension(this.FileName) + ".xml");
@@ -264,7 +272,7 @@ namespace ChanSort.Loader.Philips
var setupNode = node["Setup"] ?? throw new FileLoadException("Missing Setup XML element");
var bcastNode = node["Broadcast"] ?? throw new FileLoadException("Missing Broadcast XML element");
var fname = Path.GetFileNameWithoutExtension(file.path).ToLower();
var fname = Path.GetFileNameWithoutExtension(file.path).ToLowerInvariant();
var medium = bcastNode.GetAttribute("medium");
if (medium == "" && fname.Length >= 4 && fname.StartsWith("dvb"))
medium = fname;
@@ -276,9 +284,22 @@ namespace ChanSort.Loader.Philips
list.VisibleColumnFieldNames.Add("ServiceTypeName");
}
if (setupNode.HasAttribute("ChannelName"))
if (setupNode.HasAttribute("name"))
{
file.formatVersion = 1;
this.Features.FavoritesMode = FavoritesMode.None;
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove("Favorites");
list.VisibleColumnFieldNames.Remove("Lock");
list.VisibleColumnFieldNames.Remove("Hidden");
list.VisibleColumnFieldNames.Remove("ServiceTypeName");
list.VisibleColumnFieldNames.Remove("Encrypted");
}
}
else if (setupNode.HasAttribute("ChannelName"))
{
file.formatVersion = 2;
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
this.Features.MaxFavoriteLists = 1;
@@ -292,19 +313,6 @@ namespace ChanSort.Loader.Philips
hasEncrypt = setupNode.HasAttribute("Scrambled");
}
else if (setupNode.HasAttribute("name"))
{
file.formatVersion = 2;
this.Features.FavoritesMode = FavoritesMode.None;
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove("Favorites");
list.VisibleColumnFieldNames.Remove("Lock");
list.VisibleColumnFieldNames.Remove("Hidden");
list.VisibleColumnFieldNames.Remove("ServiceTypeName");
list.VisibleColumnFieldNames.Remove("Encrypted");
}
}
else
throw new FileLoadException("Unknown data format");
@@ -353,9 +361,9 @@ namespace ChanSort.Loader.Philips
chan.OldProgramNr = -1;
chan.IsDeleted = false;
if (file.formatVersion == 1)
this.ParseChannelFormat1(data, chan);
this.ParseRepairFormat(data, chan);
else if (file.formatVersion == 2)
this.ParseChannelFormat2(data, chan);
this.ParseChannelMapFormat(data, chan);
if ((chan.SignalSource & SignalSource.MaskAdInput) == SignalSource.DvbT)
chan.ChannelOrTransponder = LookupData.Instance.GetDvbtTransponder(chan.FreqInMhz).ToString();
@@ -366,10 +374,30 @@ namespace ChanSort.Loader.Philips
}
#endregion
#region ParseChannelFormat1
private void ParseChannelFormat1(Dictionary<string,string> data, Channel chan)
#region ParseRepairFormat
private void ParseRepairFormat(Dictionary<string, string> data, Channel chan)
{
chan.Format = 1;
chan.OldProgramNr = ParseInt(data.TryGet("presetnumber"));
chan.Name = data.TryGet("name");
chan.RawName = chan.Name;
chan.FreqInMhz = ParseInt(data.TryGet("frequency"));
//if ((chan.SignalSource & SignalSource.Analog) != 0) // analog channels have some really strange values (e.g. 00080 - 60512) that I can't convert to a plausible freq range (48-856 MHz)
// chan.FreqInMhz /= 16;
if (chan.FreqInMhz > 1200 && (chan.SignalSource & SignalSource.Sat) == 0)
chan.FreqInMhz /= 1000;
chan.ServiceId = ParseInt(data.TryGet("serviceID"));
chan.OriginalNetworkId = ParseInt(data.TryGet("ONID"));
chan.TransportStreamId = ParseInt(data.TryGet("TSID"));
chan.ServiceType = ParseInt(data.TryGet("serviceType"));
chan.SymbolRate = ParseInt(data.TryGet("symbolrate")) / 1000;
}
#endregion
#region ParseChannelMapFormat
private void ParseChannelMapFormat(Dictionary<string,string> data, Channel chan)
{
chan.Format = 2;
chan.RawSatellite = data.TryGet("SatelliteName");
chan.Satellite = DecodeName(chan.RawSatellite);
chan.OldProgramNr = ParseInt(data.TryGet("ChannelNumber"));
@@ -410,26 +438,6 @@ namespace ChanSort.Loader.Philips
}
#endregion
#region ParseChannelFormat2
private void ParseChannelFormat2(Dictionary<string, string> data, Channel chan)
{
chan.Format = 2;
chan.OldProgramNr = ParseInt(data.TryGet("presetnumber"));
chan.Name = data.TryGet("name");
chan.RawName = chan.Name;
chan.FreqInMhz = ParseInt(data.TryGet("frequency"));
//if ((chan.SignalSource & SignalSource.Analog) != 0) // analog channels have some really strange values (e.g. 00080 - 60512) that I can't convert to a plausible freq range (48-856 MHz)
// chan.FreqInMhz /= 16;
if (chan.FreqInMhz > 1200 && (chan.SignalSource & SignalSource.Sat) == 0)
chan.FreqInMhz /= 1000;
chan.ServiceId = ParseInt(data.TryGet("serviceID"));
chan.OriginalNetworkId = ParseInt(data.TryGet("ONID"));
chan.TransportStreamId = ParseInt(data.TryGet("TSID"));
chan.ServiceType = ParseInt(data.TryGet("serviceType"));
chan.SymbolRate = ParseInt(data.TryGet("symbolrate")) / 1000;
}
#endregion
#region ReadFavList
private void ReadFavList(XmlNode node)
{
@@ -542,15 +550,25 @@ namespace ChanSort.Loader.Philips
}
if (ch.Format == 1)
this.UpdateChannelFormat1(ch, setFavoriteNumber);
this.UpdateRepairFormat(ch);
else if (ch.Format == 2)
this.UpdateChannelFormat2(ch);
this.UpdateChannelMapFormat(ch, setFavoriteNumber);
}
}
#endregion
#region UpdateChannelFormat1 and 2
private void UpdateChannelFormat1(Channel ch, bool setFavoriteNumber)
#region UpdateRepairFormat()
private void UpdateRepairFormat(Channel ch)
{
ch.SetupNode.Attributes["presetnumber"].Value = ch.NewProgramNr.ToString();
if (ch.IsNameModified)
ch.SetupNode.Attributes["name"].Value = ch.Name;
}
#endregion
#region UpdateChannelMapFormat()
private void UpdateChannelMapFormat(Channel ch, bool setFavoriteNumber)
{
ch.SetupNode.Attributes["ChannelNumber"].Value = ch.NewProgramNr.ToString();
@@ -574,18 +592,12 @@ namespace ChanSort.Loader.Philips
}
}
private void UpdateChannelFormat2(Channel ch)
{
ch.SetupNode.Attributes["presetnumber"].Value = ch.NewProgramNr.ToString();
if (ch.IsNameModified)
ch.SetupNode.Attributes["name"].Value = ch.Name;
}
#endregion
#region UpdateFavList
private void UpdateFavList()
{
var favFile = this.fileDataList.FirstOrDefault(fd => Path.GetFileName(fd.path).ToLower() == "favorite.xml");
var favFile = this.fileDataList.FirstOrDefault(fd => Path.GetFileName(fd.path).ToLowerInvariant() == "favorite.xml");
if (favFile == null)
return;
@@ -644,7 +656,7 @@ namespace ChanSort.Loader.Philips
#region ReorderNodes
private void ReorderNodes(FileData file)
{
if (file.formatVersion != 1)
if (file.formatVersion != 2)
return;
var nodes = file.doc.DocumentElement.GetElementsByTagName("Channel");