- 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)
- Philips: fixed special characters in channel names (e.g. german umlauts)
- Philips: "ServiceType" now only shows "TV" or "Radio". There is no information about HD/SD in the file.
This commit is contained in:
Horst Beham
2021-01-17 15:44:45 +01:00
parent fd603ac8ec
commit c9fb32f40b
15 changed files with 394 additions and 405 deletions

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -9,6 +11,9 @@ using ChanSort.Api;
namespace ChanSort.Loader.Philips
{
/*
This loader handles the file format versions 1.x (*Table and *.dat files) and version 25.x-45.x (*Db.bin files)
channellib\CableDigSrvTable:
===========================
Channels in this file are not physically ordered by the program number and there is no linked list with prev/next indexes.
@@ -92,37 +97,42 @@ namespace ChanSort.Loader.Philips
#region Load()
public override void Load()
{
if (!SetFileNameToChanLstBin())
throw new FileLoadException("Unsupported folder structure. Required files are:\n"
+ "ChannelList\\chanLst.bin\n"
+ "ChannelList\\channellib\\CableDigSrvTable\n"
+ "ChannelList\\s2channellib\\service.dat");
this.chanLstBin = new ChanLstBin();
this.chanLstBin.Load(this.FileName, msg => this.logMessages.AppendLine(msg));
this.dataFilePaths.Add(this.FileName);
if (chanLstBin.VersionMajor >= 25 && chanLstBin.VersionMajor <= 45) // need VC2010 Redist for the SQLite library
DepencencyChecker.AssertVc2010RedistPackageX86Installed();
var dir = Path.GetDirectoryName(this.FileName) ?? "";
var channellib = Path.Combine(dir, "channellib");
var s2channellib = Path.Combine(dir, "s2channellib");
// channellib files for DVB-C/T in version 1.1 and 1.2
LoadDvbCT(dvbtChannels, Path.Combine(channellib, "AntennaDigSrvTable"), "CableDigSrvTable_entry");
LoadDvbCTPresets(dvbtChannels, Path.Combine(channellib, "AntennaPresetTable"));
LoadDvbCT(dvbcChannels, Path.Combine(channellib, "CableDigSrvTable"), "CableDigSrvTable_entry");
LoadDvbCTPresets(dvbcChannels, Path.Combine(channellib, "CablePresetTable"));
if (chanLstBin.VersionMajor <= 11)
{
LoadDvbCT(dvbtChannels, Path.Combine(channellib, "AntennaDigSrvTable"), "CableDigSrvTable_entry");
LoadDvbCTPresets(dvbtChannels, Path.Combine(channellib, "AntennaPresetTable"));
LoadDvbCT(dvbcChannels, Path.Combine(channellib, "CableDigSrvTable"), "CableDigSrvTable_entry");
LoadDvbCTPresets(dvbcChannels, Path.Combine(channellib, "CablePresetTable"));
// s2channellib files for DVB-S in version 1.1 and 1.2
LoadDvbsSatellites(Path.Combine(s2channellib, "satellite.dat"));
LoadDvbsTransponders(Path.Combine(s2channellib, "tuneinfo.dat"));
LoadDvbS(satChannels, Path.Combine(s2channellib, "service.dat"), "service.dat_entry");
LoadDvbsFavorites(Path.Combine(s2channellib, "favorite.dat"));
var db_file_info = Path.Combine(s2channellib, "db_file_info.dat");
if (File.Exists(db_file_info))
this.dataFilePaths.Add(db_file_info);
// version 45
if (chanLstBin.VersionMajor == 45)
LoadDvbS(satChannels, Path.Combine(s2channellib, "SatelliteDb.bin"), "Map45_SatelliteDb_entry");
LoadDvbsSatellites(Path.Combine(s2channellib, "satellite.dat"));
LoadDvbsTransponders(Path.Combine(s2channellib, "tuneinfo.dat"));
LoadDvbS(satChannels, Path.Combine(s2channellib, "service.dat"), "service.dat_entry");
LoadDvbsFavorites(Path.Combine(s2channellib, "favorite.dat"));
var db_file_info = Path.Combine(s2channellib, "db_file_info.dat");
if (File.Exists(db_file_info))
this.dataFilePaths.Add(db_file_info);
}
else if (chanLstBin.VersionMajor >= 25 && chanLstBin.VersionMajor <= 45)
{
// version 25-45
LoadDvbCT(dvbtChannels, Path.Combine(channellib, "TerrestrialDb.bin"), "Map45_CableDb.bin_entry");
LoadDvbCT(dvbcChannels, Path.Combine(channellib, "CableDb.bin"), "Map45_CableDb.bin_entry");
LoadDvbS(satChannels, Path.Combine(s2channellib, "SatelliteDb.bin"), "Map45_SatelliteDb.bin_entry");
var tvDbFile = Path.Combine(dir, "tv.db");
if (File.Exists(tvDbFile))
this.dataFilePaths.Add(tvDbFile);
}
// for a proper ChanSort backup/restore with .bak files, the Philips _backup.dat files must also be included
foreach (var file in this.dataFilePaths.ToList())
@@ -131,36 +141,8 @@ namespace ChanSort.Loader.Philips
this.dataFilePaths.Add(file.Replace(".dat", "_backup.dat"));
}
}
#endregion
#region SetFileNameToChanLstBin()
private bool SetFileNameToChanLstBin()
{
var dir = Path.GetDirectoryName(this.FileName) ?? "";
var dirName = Path.GetFileName(dir);
if (StringComparer.InvariantCultureIgnoreCase.Compare(dirName, "channellib") == 0 || StringComparer.InvariantCultureIgnoreCase.Compare(dirName, "s2channellib") == 0)
{
dir = Path.GetDirectoryName(dir) ?? "";
dirName = Path.GetFileName(dir);
}
if (StringComparer.InvariantCultureIgnoreCase.Compare(dirName, "ChannelList") != 0)
return false;
var chanLstBin = Path.Combine(dir, "chanLst.bin");
if (!File.Exists(chanLstBin))
return false;
if (!File.Exists(Path.Combine(dir, "channellib", "CableDigSrvTable")))
return false;
if (!File.Exists(Path.Combine(dir, "s2channellib", "service.dat")))
return false;
this.FileName = chanLstBin; // this file is used as a fixed reference point for the whole directory structure
return true;
}
#endregion
#region LoadDvbCT
private void LoadDvbCT(ChannelList list, string path, string mappingName)
@@ -169,7 +151,7 @@ namespace ChanSort.Loader.Philips
return;
var mapping = new DataMapping(this.ini.GetSection(mappingName));
mapping.SetDataPtr(data, 20);
mapping.SetDataPtr(data, chanLstBin.VersionMajor <= 11 ? 20 : 12);
for (int i = 0; i < recordCount; i++, mapping.BaseOffset += recordSize)
{
@@ -187,13 +169,17 @@ namespace ChanSort.Loader.Philips
}
string channelName = Encoding.Unicode.GetString(data, offChannelName, lenName);
var checksum = mapping.GetDword("offChecksum");
mapping.SetDword("offChecksum", 0);
var crc = FaultyCrc32(data, mapping.BaseOffset + mapping.GetConst("offChecksum", 0), recordSize);
if (crc != checksum)
throw new FileLoadException($"Invalid CRC in record {i} in {path}");
if (chanLstBin.VersionMajor <= 11)
{
var checksum = mapping.GetDword("offChecksum");
mapping.SetDword("offChecksum", 0);
var crc = FaultyCrc32(data, mapping.BaseOffset + mapping.GetConst("offChecksum", 0), recordSize);
if (crc != checksum)
throw new FileLoadException($"Invalid CRC in record {i} in {path}");
}
var ch = new Channel(list.SignalSource, i, progNr, channelName);
ch.Id = mapping.GetWord("offId"); // only relevant for ChannelMap45
ch.FreqInMhz = (decimal) mapping.GetWord("offFreqTimes16") / 16;
ch.OriginalNetworkId = mapping.GetWord("offOnid");
ch.TransportStreamId = mapping.GetWord("offTsid");
@@ -254,13 +240,27 @@ namespace ChanSort.Loader.Philips
return false;
data = File.ReadAllBytes(path);
if (data.Length < 20)
return false;
if (chanLstBin.VersionMajor <= 11)
{
if (data.Length < 20)
return false;
recordSize = BitConverter.ToInt32(data, 8);
recordCount = BitConverter.ToInt32(data, 12);
if (data.Length != 20 + recordCount * recordSize)
throw new FileLoadException("Unsupported file content: " + path);
}
else
{
if (data.Length < 12)
return false;
recordSize = 156; // Map45
recordCount = BitConverter.ToInt32(data, 8);
if (data.Length != 12 + recordCount * recordSize)
throw new FileLoadException("Unsupported file content: " + path);
}
recordSize = BitConverter.ToInt32(data, 8);
recordCount = BitConverter.ToInt32(data, 12);
if (data.Length != 20 + recordCount * recordSize)
throw new FileLoadException("Unsupported file content: " + path);
this.dataFilePaths.Add(path);
return true;
@@ -296,7 +296,8 @@ namespace ChanSort.Loader.Philips
var s = new Satellite(i);
var pos = (sbyte)data[baseOffset + 8];
s.OrbitalPosition = pos < 0 ? -pos + "W" : pos + "E";
s.Name = this.DefaultEncoding.GetString(data, baseOffset + 16, 16).TrimEnd('\0');
s.Name = this.DefaultEncoding.GetString(data, baseOffset + 16, 16).TrimGarbage();
this.DataRoot.AddSatellite(s);
}
}
@@ -350,10 +351,12 @@ namespace ChanSort.Loader.Philips
return;
var data = File.ReadAllBytes(path);
if (data.Length < 4)
if (data.Length < 12)
return;
if (chanLstBin.VersionMajor == 1)
var version = chanLstBin.VersionMajor;
if (version <= 11)
{
var checksum = BitConverter.ToUInt32(data, data.Length - 4);
@@ -366,8 +369,10 @@ namespace ChanSort.Loader.Philips
int recordSize = BitConverter.ToInt32(data, 4);
int recordCount = BitConverter.ToInt32(data, 8);
if (recordSize == 0 && version != 1)
recordSize = recordCount == 0 ? 0 : (data.Length - 12) / recordCount;
if (chanLstBin.VersionMajor == 1)
if (chanLstBin.VersionMajor <= 11)
{
// 12 bytes header, then a "next/prev" table, then the service records, then a CRC32
// the "next/prev" table is a ring-list, every entry consists of 2 ushorts with the next and previous channel, wrapping around on the ends
@@ -380,7 +385,7 @@ namespace ChanSort.Loader.Philips
var dvbStringDecoder = new DvbStringDecoder(this.DefaultEncoding);
var mapping = new DataMapping(this.ini.GetSection(mappingName));
mapping.SetDataPtr(data, 12 + recordCount * 4);
mapping.SetDataPtr(data, 12 + (chanLstBin.VersionMajor <= 11 ? recordCount * 4 : 0));
for (int i = 0; i < recordCount; i++, mapping.BaseOffset += recordSize)
{
var ch = LoadDvbsChannel(list, mapping, i, dvbStringDecoder);
@@ -394,7 +399,7 @@ namespace ChanSort.Loader.Philips
{
var transponderId = mapping.GetWord("offTransponderIndex");
var progNr = mapping.GetWord("offProgNr");
var ch = new ChannelInfo(list.SignalSource, recordIndex, progNr, null);
var ch = new Channel(list.SignalSource, recordIndex, progNr, "");
// deleted channels must be kept in the list because their records must also be physically reordered when saving the list
if (progNr == 0xFFFF || transponderId == 0xFFFF)
@@ -413,14 +418,24 @@ namespace ChanSort.Loader.Philips
ch.VideoPid = mapping.GetWord("offVpid") & mapping.GetMask("maskVpid");
ch.Favorites = mapping.GetFlag("IsFav") ? Favorites.A : 0;
ch.OldProgramNr = progNr;
ch.Id = mapping.GetWord("offId"); // relevant for ChannelMap45
// the 0x1F as the first byte of the channel name is likely the DVB encoding indicator for UTF-8. So we use the DvbStringDecoder here
dvbStringDecoder.GetChannelNames(mapping.Data, mapping.BaseOffset + mapping.GetConst("offName", 0), mapping.GetConst("lenName", 0), out var longName, out var shortName);
ch.Name = longName.TrimEnd('\0');
ch.ShortName = shortName.TrimEnd('\0');
if (chanLstBin.VersionMajor <= 11)
{
// the 0x1F as the first byte of the channel name is likely the DVB encoding indicator for UTF-8. So we use the DvbStringDecoder here
dvbStringDecoder.GetChannelNames(mapping.Data, mapping.BaseOffset + mapping.GetConst("offName", 0), mapping.GetConst("lenName", 0), out var longName, out var shortName);
ch.Name = longName;
ch.ShortName = shortName;
}
else
{
ch.Name = Encoding.Unicode.GetString(mapping.Data, mapping.BaseOffset + mapping.GetConst("offName", 0), mapping.GetConst("lenName", 0)).TrimEnd('\0');
ch.FreqInMhz = mapping.GetWord("offFreq");
ch.SymbolRate = (int)(mapping.GetDword("offSymbolRate") / 1000);
}
dvbStringDecoder.GetChannelNames(mapping.Data, mapping.BaseOffset + mapping.GetConst("offProvider", 0), mapping.GetConst("lenProvider", 0), out var provider, out _);
ch.Provider = provider.TrimEnd('\0');
ch.Provider = provider;
// copy values from the satellite/transponder tables to the channel
if (this.DataRoot.Transponder.TryGetValue(transponderId, out var t))
@@ -495,14 +510,25 @@ namespace ChanSort.Loader.Philips
var channellib = Path.Combine(dir, "channellib");
var s2channellib = Path.Combine(dir, "s2channellib");
SaveDvbCTChannels(this.dvbtChannels, Path.Combine(channellib, "AntennaDigSrvTable"));
SaveDvbCTPresets(this.dvbtChannels, Path.Combine(channellib, "AntennaPresetTable"));
SaveDvbCTChannels(this.dvbcChannels, Path.Combine(channellib, "CableDigSrvTable"));
SaveDvbCTPresets(this.dvbcChannels, Path.Combine(channellib, "CablePresetTable"));
if (chanLstBin.VersionMajor <= 11)
{
SaveDvbCTChannels(this.dvbtChannels, Path.Combine(channellib, "AntennaDigSrvTable"));
SaveDvbCTPresets(this.dvbtChannels, Path.Combine(channellib, "AntennaPresetTable"));
SaveDvbCTChannels(this.dvbcChannels, Path.Combine(channellib, "CableDigSrvTable"));
SaveDvbCTPresets(this.dvbcChannels, Path.Combine(channellib, "CablePresetTable"));
SaveDvbsChannels(Path.Combine(s2channellib, "service.dat"));
SaveDvbsFavorites(Path.Combine(s2channellib, "favorite.dat"));
SaveDvbsDbFileInfo(Path.Combine(s2channellib, "db_file_info.dat"));
SaveDvbsChannels(Path.Combine(s2channellib, "service.dat"));
SaveDvbsFavorites(Path.Combine(s2channellib, "favorite.dat"));
SaveDvbsDbFileInfo(Path.Combine(s2channellib, "db_file_info.dat"));
}
else if (chanLstBin.VersionMajor >= 25 && chanLstBin.VersionMajor <= 45)
{
SaveDvbCTChannels(this.dvbtChannels, Path.Combine(channellib, "TerrestrialDb.bin"));
SaveDvbCTChannels(this.dvbcChannels, Path.Combine(channellib, "CableDb.bin"));
SaveDvbsChannels(Path.Combine(s2channellib, "SatelliteDb.bin"));
UpdateChannelMap45TvDb();
}
this.chanLstBin.Save(this.FileName);
}
@@ -515,18 +541,38 @@ namespace ChanSort.Loader.Philips
if (!ReadAndValidateChannellibFile(path, out var data, out var recordSize, out _))
return;
var mapping = new DataMapping(this.ini.GetSection("CableDigSrvTable_entry"));
mapping.SetDataPtr(data, 20);
int baseOffset;
DataMapping mapping;
if (chanLstBin.VersionMajor <= 11)
{
mapping = new DataMapping(this.ini.GetSection("CableDigSrvTable_entry"));
baseOffset = 20;
}
else
{
mapping = new DataMapping(this.ini.GetSection("Map45_CableDb.bin_entry"));
baseOffset = 12;
}
mapping.SetDataPtr(data, baseOffset);
foreach (var ch in list.Channels)
{
mapping.BaseOffset = 20 + (int)ch.RecordIndex * recordSize;
if (ch.IsProxy) continue;
mapping.BaseOffset = baseOffset + (int)ch.RecordIndex * recordSize;
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetByte("offLocked", ch.Lock ? 1 : 0);
mapping.SetByte("offIsFav", ch.Favorites == 0 ? 0 : 1);
mapping.SetDword("offChecksum", 0);
var crc = FaultyCrc32(data, mapping.BaseOffset, recordSize);
mapping.SetDword("offChecksum", crc);
if (chanLstBin.VersionMajor <= 11)
{
mapping.SetDword("offChecksum", 0);
var crc = FaultyCrc32(data, mapping.BaseOffset, recordSize);
mapping.SetDword("offChecksum", crc);
}
else if (chanLstBin.VersionMajor >= 25 && chanLstBin.VersionMajor <= 45)
{
mapping.SetWord("offServiceEdit", 1);
}
}
File.WriteAllBytes(path, data);
@@ -564,16 +610,32 @@ namespace ChanSort.Loader.Philips
private void SaveDvbsChannels(string path)
{
var orig = File.ReadAllBytes(path);
int recordSize = BitConverter.ToInt32(orig, 4);
int recordCount = BitConverter.ToInt32(orig, 8);
// create a new array for the modified data, copying the header and next/prev table
var data = new byte[orig.Length];
Array.Copy(orig, data, 12 + recordCount * 4);
var baseOffset = 12 + recordCount * 4;
int recordCount = BitConverter.ToInt32(orig, 8);
int recordSize;
int baseOffset;
DataMapping mapping;
var mapping = new DataMapping(this.ini.GetSection("service.dat_entry"));
if (chanLstBin.VersionMajor <= 11)
{
recordSize = BitConverter.ToInt32(orig, 4);
baseOffset = 12 + recordCount * 4;
mapping = new DataMapping(this.ini.GetSection("service.dat_entry"));
}
else
{
recordSize = recordCount == 0 ? 0 : (orig.Length - 12) / recordCount;
baseOffset = 12;
mapping = new DataMapping(this.ini.GetSection("Map45_SatelliteDb.bin_entry"));
}
if (recordCount == 0)
return;
Array.Copy(orig, data, baseOffset);
mapping.SetDataPtr(data, baseOffset);
// copy physical records to bring them in the new order and update fields like progNr
@@ -594,18 +656,22 @@ namespace ChanSort.Loader.Philips
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetFlag("IsFav", ch.Favorites != 0);
mapping.SetFlag("Locked", ch.Lock);
mapping.SetWord("offServiceEdit", 1);
}
ch.RecordIndex = i++; // required so that subsequent saves don't reshuffle the records
}
if (chanLstBin.VersionMajor <= 11)
{
var crc32 = ~Crc32.Reversed.CalcCrc32(data, 0, data.Length - 4);
data.SetInt32(data.Length - 4, (int) crc32);
var backupFile = path.Replace(".dat", "_backup.dat");
File.WriteAllBytes(backupFile, data);
}
var crc32 = ~Crc32.Reversed.CalcCrc32(data, 0, data.Length - 4);
data.SetInt32(data.Length-4, (int)crc32);
File.WriteAllBytes(path, data);
var backupFile = path.Replace(".dat", "_backup.dat");
File.WriteAllBytes(backupFile, data);
}
#endregion
@@ -667,6 +733,47 @@ namespace ChanSort.Loader.Philips
}
#endregion
#region UpdateChannelMap45TvDb()
private void UpdateChannelMap45TvDb()
{
var tvDb = Path.Combine(Path.GetDirectoryName(this.FileName) ?? "", "tv.db");
if (!File.Exists(tvDb))
return;
using var conn = new SQLiteConnection($"Data Source={tvDb}");
conn.Open();
using var trans = conn.BeginTransaction();
using var cmd = conn.CreateCommand();
cmd.CommandText = "update channels set display_number=@prNum, display_name=@name, browsable=@browsable, locked=@locked where _id=@id";
cmd.Parameters.Clear();
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int32));
cmd.Parameters.Add(new SQLiteParameter("@prNum", DbType.Int32));
cmd.Parameters.Add(new SQLiteParameter("@name", DbType.String));
cmd.Parameters.Add(new SQLiteParameter("@browsable", DbType.Int32));
cmd.Parameters.Add(new SQLiteParameter("@locked", DbType.Int32));
cmd.Prepare();
foreach (var list in this.DataRoot.ChannelLists)
{
foreach (var chan in list.Channels)
{
if (!(chan is Channel ch))
continue;
cmd.Parameters["@id"].Value = ch.Id;
cmd.Parameters["@prNum"].Value = ch.NewProgramNr;
cmd.Parameters["@name"].Value = ch.Name;
cmd.Parameters["@browsable"].Value = ch.Skip ? 0 : 1;
cmd.Parameters["@locked"].Value = ch.Lock ? 1 : 0;
var res = cmd.ExecuteNonQuery();
if (res == 0)
this.logMessages.AppendFormat($"Could not update record with id {ch.Id} in tv.db service table");
}
}
trans.Commit();
conn.Close();
}
#endregion
#region FaultyCrc32
public static uint FaultyCrc32(byte[] bytes, int start, int count)

View File

@@ -54,7 +54,7 @@ namespace ChanSort.Loader.Philips
var modelNameLen = BitConverter.ToInt32(content, off);
off += 4 + modelNameLen;
var baseDir = Path.GetDirectoryName(path);
var baseDir = Path.GetDirectoryName(path) ?? "";
var relPath = "/channellib/";
while (off < content.Length)
{
@@ -67,6 +67,7 @@ namespace ChanSort.Loader.Philips
else
{
// normally all files after the /s2channellib/ entry are inside that folder, but "Favorite.xml" is in the main folder
// in ChannelMap45 there is also tv.db and list.db in the main folder
var newPath = relPath + fileName;
if (!File.Exists(Path.Combine(baseDir, newPath)) && File.Exists(Path.Combine(baseDir, fileName)))
newPath = "/" + fileName;
@@ -92,9 +93,18 @@ namespace ChanSort.Loader.Philips
var filePath = baseDir + entry.Key;
if (!File.Exists(filePath))
{
errors += $"\nchanLst.bin: file not found in directory structure: {entry.Key}";
continue;
}
var data = File.ReadAllBytes(filePath);
var length = Math.Min(data.Length, VersionMajor <= 12 ? 0x6000 : 0x145A00);
var length = data.Length;
if (VersionMajor < 12 && length > 0x6000)
length = 0x6000; // there might be another cap at 0x013FA000 + 0x6000 in some versions
//if (length > 0x0140000)
// length = 0x0140000;
var actualCrc = Crc16.Calc(data, 0, length);
if (actualCrc != expectedCrc)
{
@@ -104,10 +114,7 @@ namespace ChanSort.Loader.Philips
}
if (errors != "")
{
this.log.Invoke(errors);
//throw new FileLoadException(errors);
}
this.log?.Invoke(errors);
}
public void Save(string chanLstBinPath)
@@ -117,7 +124,9 @@ namespace ChanSort.Loader.Philips
{
var path = baseDir + entry.Key;
var data = File.ReadAllBytes(path);
var length = Math.Min(data.Length, VersionMajor <= 12 ? 0x6000 : 0x145A00);
var length = data.Length;
if (VersionMajor < 12 && length > 0x6000)
length = 0x6000; // there might be another cap at 0x013FA000 + 0x6000 in some versions
var crc = Crc16.Calc(data, 0, length);
var off = entry.Value;
content[off] = (byte) crc;

View File

@@ -56,11 +56,13 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SQLite">
<HintPath>..\DLL\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@@ -79,9 +81,9 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="ChanSort.Loader.Philips.ini">
<None Include="ChanSort.Loader.Philips.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -47,7 +47,7 @@ offFreq=18
# ChannelMap_45 format
[Map45_SatelliteDbBin_entry]
[Map45_CableDb.bin_entry]
offId=0
offFreq=4
offProgNr=8
@@ -61,7 +61,26 @@ offScrambleStat=36
offLocked=40
offModulateion=44
offServiceType=52
offServiceEdit=58
offName=88
lenName=64
[Map45_SatelliteDb.bin_entry]
offId=0
offFreq=4
offProgNr=8
offAnalogUid=12
offOnid=16
offTsid=20
offSid=24
offSymbolRate=28
offLogoNr=32
offScrambleStat=36
offLocked=40
offModulateion=44
offServiceType=52
offServiceEdit=58
offName=80
lenName=64
offSatName=146
lenSatName=64
lenSatName=64

View File

@@ -7,6 +7,7 @@ namespace ChanSort.Loader.Philips
{
public Channel(SignalSource source, long index, int oldProgNr, string name) : base(source, index, oldProgNr, name)
{
this.RecordOrder = (int)index;
}
internal Channel(SignalSource source, int order, int rowId, XmlNode setupNode)
@@ -27,6 +28,7 @@ namespace ChanSort.Loader.Philips
public string RawName;
public string RawSatellite;
public int Format;
public int Id; // links entries in the ChannelMap45/*Db.bin files with the entries in the tv.db channels table
}
}

View File

@@ -29,7 +29,7 @@ namespace ChanSort.Loader.Philips
* e.g. 32PFL5806K/02, 42PFL7656K/02
*
* version 1.2
* same as version 1.1
* same as version 1.1 for most parts, but different tuneinfo.dat format
* e.g. 32PFL5507K/12, 42PFL4317K/12, 32PFL5507K/12
*
* version 11.1
@@ -112,10 +112,10 @@ namespace ChanSort.Loader.Philips
if (majorVersion == 0 || majorVersion >= 100 && majorVersion <= 110)
return new XmlSerializer(inputFile);
if (majorVersion == 1)
if (majorVersion == 1 || majorVersion == 45) // || majorVersion == 11 // format version 11 is similar to 1.x, but not (yet) supported
return new BinarySerializer(inputFile);
throw new FileLoadException($"Selected file must be either chanLst.bin or CM_*.xml/.bin");
throw new FileLoadException($"Philips ChannelMap format version {majorVersion} is not supported.");
}
}
}

View File

@@ -245,6 +245,12 @@ namespace ChanSort.Loader.Philips
medium = fname;
bool hasEncrypt = false;
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove("ServiceType");
list.VisibleColumnFieldNames.Add("ServiceTypeName");
}
if (setupNode.HasAttribute("ChannelName"))
{
file.formatVersion = 1;
@@ -271,7 +277,6 @@ namespace ChanSort.Loader.Philips
list.VisibleColumnFieldNames.Remove("Favorites");
list.VisibleColumnFieldNames.Remove("Lock");
list.VisibleColumnFieldNames.Remove("Hidden");
list.VisibleColumnFieldNames.Remove("ServiceType");
list.VisibleColumnFieldNames.Remove("ServiceTypeName");
list.VisibleColumnFieldNames.Remove("Encrypted");
}
@@ -354,11 +359,9 @@ namespace ChanSort.Loader.Philips
chan.TransportStreamId = ParseInt(data.TryGet("Tsid"));
chan.ServiceId = ParseInt(data.TryGet("Sid"));
chan.FreqInMhz = ParseInt(data.TryGet("Frequency")); ;
if (chan.FreqInMhz > 2000)
if (chan.FreqInMhz > 2000 && (chan.SignalSource & SignalSource.Sat) == 0)
chan.FreqInMhz /= 1000;
if (chan.FreqInMhz > 2000)
chan.FreqInMhz /= 1000;
chan.ServiceType = ParseInt(data.TryGet("ServiceType"));
chan.ServiceTypeName = ParseInt(data.TryGet("ServiceType")) == 1 ? "TV" : "Radio";
var decoderType = data.TryGet("DecoderType");
if (decoderType == "1")
chan.Source = "DVB-T";
@@ -366,6 +369,8 @@ namespace ChanSort.Loader.Philips
chan.Source = "DVB-C";
chan.SignalSource |= LookupData.Instance.IsRadioTvOrData(chan.ServiceType);
chan.SymbolRate = ParseInt(data.TryGet("SymbolRate"));
if (chan.SymbolRate > 100000) // DVB-S stores values in kSym, DVB-C stores it in Sym, DVB-T stores 0
chan.SymbolRate /= 1000;
if (data.TryGetValue("Polarization", out var pol))
chan.Polarity = pol == "0" ? 'H' : 'V';
chan.Hidden |= data.TryGet("SystemHidden") == "1";
@@ -382,9 +387,9 @@ namespace ChanSort.Loader.Philips
chan.Name = data.TryGet("name");
chan.RawName = chan.Name;
chan.FreqInMhz = ParseInt(data.TryGet("frequency"));
//if ((chan.SignalSource & SignalSource.Analog) != 0)
//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)
if (chan.FreqInMhz > 1200 && (chan.SignalSource & SignalSource.Sat) == 0)
chan.FreqInMhz /= 1000;
chan.ServiceId = ParseInt(data.TryGet("serviceID"));
chan.OriginalNetworkId = ParseInt(data.TryGet("ONID"));
@@ -444,42 +449,12 @@ namespace ChanSort.Loader.Philips
foreach (var part in hexParts)
{
if (part == "" || part == "0x00")
if (part == "")
continue;
buffer.WriteByte((byte)ParseInt(part));
}
return this.DefaultEncoding.GetString(buffer.GetBuffer(), 0, (int)buffer.Length);
}
#endregion
#region DefaultEncoding
public override Encoding DefaultEncoding
{
get => base.DefaultEncoding;
set
{
if (value == this.DefaultEncoding)
return;
base.DefaultEncoding = value;
this.ChangeEncoding();
}
}
#endregion
#region ChangeEncoding
private void ChangeEncoding()
{
foreach (var list in this.DataRoot.ChannelLists)
{
foreach (var channel in list.Channels)
{
if (!(channel is Channel ch))
continue;
ch.Name = this.DecodeName(ch.RawName);
ch.Satellite = this.DecodeName(ch.RawSatellite);
}
}
return Encoding.Unicode.GetString(buffer.GetBuffer(), 0, (int) buffer.Length).TrimEnd('\x0');
}
#endregion
@@ -518,7 +493,7 @@ namespace ChanSort.Loader.Philips
{
// by default .NET reformats the whole XML. These settings produce almost same format as the TV xml files use
var xmlSettings = new XmlWriterSettings();
xmlSettings.Encoding = this.DefaultEncoding;
xmlSettings.Encoding = new UTF8Encoding(false);
xmlSettings.CheckCharacters = false;
xmlSettings.Indent = true;
xmlSettings.IndentChars = " ";
@@ -616,10 +591,10 @@ namespace ChanSort.Loader.Philips
#region EncodeName
private string EncodeName(string name)
{
var bytes = this.DefaultEncoding.GetBytes(name);
var bytes = Encoding.Unicode.GetBytes(name);
var sb = new StringBuilder();
foreach (var b in bytes)
sb.Append($"0x{b:X2} 0x00 ");
sb.Append($"0x{b:X2} ");
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}