- Philips: added support for ChannelMap_115 format

- Philips: ChannelMap formats 100-115 did not always fill "Source" and "Polarity" columns correctly
- Philips: improved experimental support for Philips FLASH\_\*/\*.db file formats
  (read-only by default, can be enabled in Philips.ini for testing)
- added Polish readme and updated translation (by JakubDriver)
This commit is contained in:
Horst Beham
2021-09-19 20:28:57 +02:00
parent ece2cd7e66
commit 1afde35aca
29 changed files with 833 additions and 340 deletions

View File

@@ -17,94 +17,49 @@
; ---------------------------------------------
; mappings that are the same for all format variants
[flash_db]
reorderRecordsByChannelNumber=true
allowEdit=false
[mgr_chan_s_fta.db]
lenHeader=64
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=false
; field mappings needed to calculate variable channel record length: blockSize / (tvChannels + radioChannels)
numTvChannels=0x2C
numRadioChannels=0x30
channelBlockSize=0x3C
[mgr_chan_s_fta.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
[mgr_chan_s_pkg.db]
lenHeader=64
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=false
; field mappings needed to calculate variable channel record length: blockSize / (tvChannels+radioChannels)
numTvChannels=0x2C
numRadioChannels=0x30
channelBlockSize=0x3C
[mgr_chan_s_pkg.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
[mgr_chan_dvbt.db]
lenHeader=64
lenEntry=472
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=false
; field mappings needed to calculate variable channel record length: blockSize / (tvChannels + radioChannels)
numTvChannels=0x2C
numRadioChannels=0x30
channelBlockSize=0x3C
[mgr_chan_dvbt.db_entry]
offProgNr=0
offFav=16
offName=20
lenName=200
offProvider=224
lenProvider=200
offFreq=440
offOldProgNr=448
offRecordIndex=456
offTsid=460
offSymbolRate=462
offSid=464
offOnid=466
[mgr_chan_dvbc.db]
lenHeader=64
lenEntry=472
lenFooter=12
offFooterChecksum=8
reorderRecordsByChannelNumber=false
allowEdit=false
; field mappings needed to calculate variable channel record length: blockSize / (tvChannels + radioChannels)
numTvChannels=0x2C
numRadioChannels=0x30
channelBlockSize=0x3C
[mgr_chan_dvbc.db_entry]
[mgr.db_entry:472]
offProgNr=0
offFav=16
offName=20
@@ -119,6 +74,33 @@ offSymbolRate=462
offSid=464
offOnid=466
[mgr.db_entry:476]
offProgNr=0
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
[mgr.db_entry:480]
offProgNr=0
offFav=16
offName=20
lenName=200
offFreq=444,468
offSymbolRate=450
offOldProgNr=452
offRecordIndex=456
offTsid=460
offSid=464
offOnid=466
; ---------------------------------------------
; variant with 476 bytes per entry in mgr_chan_s_fta.db

View File

@@ -38,12 +38,13 @@ namespace ChanSort.Loader.Philips
private readonly ChannelList dvbcChannels = new ChannelList(SignalSource.DvbT, "DVB-C");
private readonly ChannelList dvbsFtaChannels = new ChannelList(SignalSource.DvbS | SignalSource.Provider0, "DVB-S FTA");
private readonly ChannelList dvbsPkgChannels = new ChannelList(SignalSource.DvbS | SignalSource.Provider1, "DVB-S Preset");
private readonly Dictionary<ChannelList, string> dbFileByList = new();
private readonly Dictionary<ChannelList, Tuple<string, int>> dbFileByList = new();
private readonly Dictionary<ChannelList, Tuple<string, int>> flashFileByList = new();
private int dvbtChannelRecordLength;
private int dvbcChannelRecordLength;
private int ftaChannelRecordLength;
private int pkgChannelRecordLength;
private readonly bool reorderPhysically;
#region ctor()
@@ -57,6 +58,10 @@ namespace ChanSort.Loader.Philips
string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini");
this.ini = new IniFile(iniFile);
var sec = ini.GetSection("flash_db");
this.reorderPhysically = sec.GetBool("reorderRecordsByChannelNumber", true);
var allowEdit = sec.GetBool("allowEdit", false);
this.DataRoot.AddChannelList(dvbtChannels);
this.DataRoot.AddChannelList(dvbcChannels);
this.DataRoot.AddChannelList(dvbsFtaChannels);
@@ -82,6 +87,7 @@ namespace ChanSort.Loader.Philips
nameof(ChannelInfo.AudioPid),
nameof(ChannelInfo.ServiceTypeName)
};
list.ReadOnly = !allowEdit;
}
}
#endregion
@@ -132,7 +138,7 @@ namespace ChanSort.Loader.Philips
LoadFlash(file, lowercaseFileName, dvbsFtaChannels, ftaChannelRecordLength);
break;
case "flash_dtvinfo_s_pkg":
if (!(dvbsFtaChannels.Count == 0 && dvbsPkgChannels.Count > 0))
if (dvbsPkgChannels.Count > 0)
LoadFlash(file, lowercaseFileName, dvbsPkgChannels, pkgChannelRecordLength);
break;
}
@@ -203,11 +209,9 @@ namespace ChanSort.Loader.Philips
throw new FileLoadException($"File {path} contains invalid checksum. Expected {expectedChecksum:x4} but calculated {actualChecksum:x4}");
channelRecordLength = lenEntry;
list.ReadOnly = !sec.GetBool("allowEdit");
var mapping = new DataMapping(this.ini.GetSection(sectionName + "_entry"));
sec = ini.GetSection("mgr_chan_s_fta.db_entry");
sec = this.ini.GetSection("mgr.db_entry:" + channelRecordLength);
var mapping = new DataMapping(sec);
var lenName = sec.GetInt("lenName");
for (int i = 0; i < records; i++)
{
@@ -236,7 +240,7 @@ namespace ChanSort.Loader.Philips
this.DataRoot.AddChannel(list, ch);
}
this.dbFileByList[list] = path;
this.dbFileByList[list] = Tuple.Create(path, channelRecordLength);
}
#endregion
@@ -459,7 +463,7 @@ namespace ChanSort.Loader.Philips
}
#endregion
public override IEnumerable<string> GetDataFilePaths() => this.dbFileByList.Values.Union(this.flashFileByList.Values.Select(tup => tup.Item1));
public override IEnumerable<string> GetDataFilePaths() => this.dbFileByList.Values.Union(this.flashFileByList.Values).Select(tup => tup.Item1);
#region Save()
public override void Save(string tvOutputFile)
@@ -468,9 +472,10 @@ namespace ChanSort.Loader.Philips
foreach (var listAndFile in this.dbFileByList)
{
var list = listAndFile.Key;
var file = listAndFile.Value;
var file = listAndFile.Value.Item1;
var lenEntry = listAndFile.Value.Item2;
var secName = Path.GetFileName(file).ToLowerInvariant();
SaveDvb(file, secName, list);
SaveDvb(file, secName, list, lenEntry);
}
// update FLASH_* files
@@ -486,30 +491,56 @@ namespace ChanSort.Loader.Philips
#endregion
#region SaveDvb()
private void SaveDvb(string file, string secName, ChannelList list)
private void SaveDvb(string file, string secName, ChannelList list, int channelRecordLength)
{
var data = File.ReadAllBytes(file);
var oldData = File.ReadAllBytes(file);
var sec = ini.GetSection(secName);
if (!GetValuesFromDvbFileHeader(sec, data, out var lenHeader, out var lenEntry, out _, out var offChecksum))
if (!GetValuesFromDvbFileHeader(sec, oldData, out var lenHeader, out var lenEntry, out _, out var offChecksum))
return;
var mapping = new DataMapping(ini.GetSection(secName + "_entry"));
foreach (var chan in list.Channels)
var newData = new byte[oldData.Length];
Array.Copy(oldData, newData, oldData.Length);
var mapping = new DataMapping(ini.GetSection("mgr.db_entry:" + channelRecordLength));
if (this.reorderPhysically)
{
if (chan is not Channel ch)
continue;
mapping.SetDataPtr(data, lenHeader + (int)ch.RecordIndex * lenEntry);
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
int newIndex = 0;
foreach (var chan in list.Channels.OrderBy(c => c.NewProgramNr).ThenBy(c => c.RecordIndex))
{
if (chan is not Channel ch)
continue;
var newOff = lenHeader + newIndex * lenEntry;
Array.Copy(oldData, lenHeader + (int)ch.RecordIndex * lenEntry, newData, newOff, lenEntry);
mapping.SetDataPtr(newData, newOff);
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
mapping.SetWord("offOldProgNr", ch.NewProgramNr);
mapping.SetWord("offRecordIndex", newIndex);
//ch.RecordIndex = newIndex; // will be updated when saving the FLASH file
++newIndex;
}
}
else
{
foreach (var chan in list.Channels.OrderBy(c => c.NewProgramNr).ThenBy(c => c.RecordIndex))
{
if (chan is not Channel ch)
continue;
var newOff = lenHeader + (int)ch.RecordIndex * lenEntry;
mapping.SetDataPtr(newData, newOff);
mapping.SetWord("offProgNr", ch.NewProgramNr);
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
}
}
// update checksum (only 16 bits are stored)
var checksum = CalcChecksum(data, 0, offChecksum);
data[offChecksum + 0] = (byte)checksum;
data[offChecksum + 1] = (byte)(checksum >> 8);
var checksum = CalcChecksum(newData, 0, offChecksum);
newData[offChecksum + 0] = (byte)checksum;
newData[offChecksum + 1] = (byte)(checksum >> 8);
File.WriteAllBytes(file, data);
File.WriteAllBytes(file, newData);
}
#endregion
@@ -523,6 +554,23 @@ namespace ChanSort.Loader.Philips
var sec = ini.GetSection(secName + ":" + dbChannelRecordLength, true);
var mapping = new DataMapping(sec, data);
// update channel index->id mapping table to match the indices in the new .db file, which is in order by the new ProgNr
if (this.reorderPhysically)
{
var off = sec.GetInt("offChannelTransponderTable");
var num = sec.GetInt("numChannelTransponderTable");
var oldTable = new byte[num * 4];
Array.Copy(data, off, oldTable, 0, oldTable.Length);
int i = 0;
foreach (var chan in list.Channels.OrderBy(c => c.NewProgramNr).ThenBy(c => c.RecordIndex))
{
if (chan is not Channel ch)
continue;
Array.Copy(oldTable, (int)ch.RecordIndex * 4, data, off + i * 4, 4);
ch.RecordIndex = i++;
}
}
// in-place update of channel data
foreach (var chan in list.Channels)
{

View File

@@ -72,8 +72,10 @@ namespace ChanSort.Loader.Philips
* same as 105.0
* e.g. 65PUS8535/12, 55PUS7334/12
*
* version 115.0
* same as 110.0
*
* Version 0.1 and 100-110 are XML based and loaded through the XmlSerializer.
* Version 0.1 and 100-115 are XML based and loaded through the XmlSerializer.
* Version 1.1 and 1.2 are loaded through the BinSerializer.
* Version 0.0, 11.1 and 45.1 are not supported yet.
*/
@@ -129,7 +131,7 @@ namespace ChanSort.Loader.Philips
}
}
if (majorVersion == 0 || majorVersion >= 100 && majorVersion <= 110)
if (majorVersion == 0 || majorVersion >= 100 && majorVersion <= 115)
return new XmlSerializer(inputFile);
if (majorVersion == 1 || majorVersion == 30 || majorVersion == 45) // || majorVersion == 11 // format version 11 is similar to 1.x, but not (yet) supported
return new BinarySerializer(inputFile);

View File

@@ -120,4 +120,26 @@
<data name="Cancel" xml:space="preserve">
<value>Anuluj</value>
</data>
<data name="InfoIgnoreImportError" xml:space="preserve">
<value>Telewizor może niepoprawnie pokazywać, że import się nie powiódł, ale można to zignorować.</value>
</data>
<data name="InfoRestartAfterImport" xml:space="preserve">
<value>INFORMACJE: Po zaimportowaniu listy z powrotem do telewizora odłącz go i podłącz ponownie po kilku sekundach.</value>
</data>
<data name="WarningChecksumErrorMsg" xml:space="preserve">
<value>OSTRZEŻENIE: W załadowanej liście wystąpiły błędy sum kontrolnych!
Istnieją 2 sytuacje, w których może się to zdarzyć:
- Wewnętrzna lista kanałów telewizora jest uszkodzona (np. po aktualizacji oprogramowania)
Można to rozwiązać, uruchamiając nowe skanowanie kanałów lub resetując telewizor.
Następnie ponownie wyeksportuj listę i otwórz ją za pomocą ChanSort.
Próba edycji i importu aktualnie uszkodzonego pliku może prowadzić do nieoczekiwanego zachowania telewizora.
- Bad USB-Stick (złe komórki pamięci lub niezgodny format pliku)
Spróbuj użyć pendrive'a &lt;= 16 GB sformatowanego w systemie FAT32 (nie NTFS ani exFAT)</value>
</data>
<data name="WarningChechsumErrorIgnore" xml:space="preserve">
<value>Zignoruj błąd i mimo to edytuj listę</value>
</data>
</root>

View File

@@ -41,8 +41,8 @@ namespace ChanSort.Loader.Philips
Nevertheless a user reported that swapping DVB-S channels 1 and 2 with Onka on a TV that uses this xml-only format 110 worked for him.
There seem to be 3 different flavors or the "100" format:
One has only .xml files in the channellib and s2channellib folders, does not indent lines in the .xml files, has a fixed number of bytes for channel and satellite names (padded with 0x00) and has no "Scramble" attribute.
And a version that has dtv_cmdb_*.bin next to the .xml files, uses 4 spaces for indentation, only writes as many bytes for names as needed and has a "Scramble" attribute.
One has only .xml files in the channellib and s2channellib folders, does not indent lines in the .xml files, has a fixed number of bytes for channel and satellite names (padded with 0x00), has no "Scramble" attribute and values 1 and 0 for "Polarization".
And a version that has dtv_cmdb_*.bin next to the .xml files, uses 4 spaces for indentation, only writes as many bytes for names as needed, has a "Scramble" attribute and uses values 1 and 2 for "Polarization".
While the first seems to work fine when XML nodes are reordered by their new programNr, the latter seems to get confused when the .bin and .xml files have different data record orders. This is still under investigation.
The Philips editor does not modify these .bin files, appends 0x00 padding to the channel names, changes indentation to 2 tabs and strips the Scramble attribute. It's likely it wasn't designed for this type of list.
@@ -89,7 +89,7 @@ namespace ChanSort.Loader.Philips
private readonly StringBuilder logMessages = new StringBuilder();
private readonly IniFile ini;
private IniFile.Section iniMapSection;
private string polarizationValueForHorizontal = "1";
#region ctor()
public XmlSerializer(string inputFile) : base(inputFile)
@@ -381,6 +381,7 @@ namespace ChanSort.Loader.Philips
{
this.iniMapSection = ini.GetSection("Map100_cmdb.bin");
this.FileFormatVersion += "/cmdb";
this.polarizationValueForHorizontal = "1"; // TODO validate
}
else if (File.Exists(Path.Combine(dir, "channelFile.bin")))
{
@@ -477,17 +478,13 @@ namespace ChanSort.Loader.Philips
else
chan.SignalSource |= SignalSource.Radio;
var decoderType = data.TryGet("DecoderType");
if (decoderType == "1")
chan.Source = "DVB-T";
else if (decoderType == "2")
chan.Source = "DVB-C";
chan.Source = (chan.SignalSource & SignalSource.Sat) != 0 ? "DVB-S" : (chan.SignalSource & SignalSource.Cable) != 0 ? "DVB-C" : (chan.SignalSource & SignalSource.Antenna) != 0 ? "DVB-T" : "";
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.Polarity = pol == polarizationValueForHorizontal ? 'H' : 'V';
chan.Hidden |= data.TryGet("SystemHidden") == "1";
chan.Encrypted = data.TryGet("Scramble") == "1" || data.TryGet("Scrambled") == "1"; // v100 sometimes contains a "Scramble", v105/v110 always contain "Scrambled"