mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-01-13 19:02:05 +01:00
- improved IniFile and Mapping to better handle missing settings
- working on philips *.db/FLASH* file format (identified 3 different variations so far)
This commit is contained in:
@@ -6,14 +6,14 @@ namespace ChanSort.Api
|
||||
public class DataMapping
|
||||
{
|
||||
protected readonly IniFile.Section settings;
|
||||
private int baseOffset;
|
||||
private byte[] data { get; set; }
|
||||
public Encoding DefaultEncoding { get; set; }
|
||||
public bool ThrowWhenAccessingUnknownSetting { get; set; } = false;
|
||||
|
||||
#region ctor()
|
||||
public DataMapping(IniFile.Section settings)
|
||||
public DataMapping(IniFile.Section settings, byte[] data = null)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.Data = data;
|
||||
this.DefaultEncoding = Encoding.Default;
|
||||
}
|
||||
#endregion
|
||||
@@ -21,19 +21,23 @@ namespace ChanSort.Api
|
||||
#region SetDataPtr(), Data, BaseOffset
|
||||
public void SetDataPtr(byte[] data, int baseOffset)
|
||||
{
|
||||
this.data = data;
|
||||
this.baseOffset = baseOffset;
|
||||
this.Data = data;
|
||||
this.BaseOffset = baseOffset;
|
||||
}
|
||||
|
||||
public byte[] Data { get { return this.data; } }
|
||||
public int BaseOffset { get { return this.baseOffset; } set { this.baseOffset = value; } }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public int BaseOffset { get; set; }
|
||||
#endregion
|
||||
|
||||
|
||||
#region GetOffsets()
|
||||
public int[] GetOffsets(string key)
|
||||
{
|
||||
return settings.GetIntList(key);
|
||||
var list= settings.GetIntList(key);
|
||||
if (list == null)
|
||||
return DefaultValue(key, Array.Empty<int>());
|
||||
return list;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -46,7 +50,7 @@ namespace ChanSort.Api
|
||||
list = settings.GetIntList("mask" + key);
|
||||
if (list != null && list.Length > 0)
|
||||
return list[0];
|
||||
return -1;
|
||||
return DefaultValue(key, -1);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -61,22 +65,22 @@ namespace ChanSort.Api
|
||||
#endregion
|
||||
|
||||
|
||||
public IniFile.Section Settings { get { return this.settings; } }
|
||||
public IniFile.Section Settings => this.settings;
|
||||
|
||||
|
||||
#region Byte
|
||||
public byte GetByte(string key)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
if (offsets.Length==0) return 0;
|
||||
return this.data[baseOffset + offsets[0]];
|
||||
if (offsets.Length==0) return DefaultValue(key, (byte)0);
|
||||
return this.Data[BaseOffset + offsets[0]];
|
||||
}
|
||||
|
||||
public void SetByte(string key, int value)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
foreach (int offset in offsets)
|
||||
this.data[baseOffset + offset] = (byte)value;
|
||||
this.Data[BaseOffset + offset] = (byte)value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -84,8 +88,8 @@ namespace ChanSort.Api
|
||||
public ushort GetWord(string key)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
if (offsets.Length == 0) return 0;
|
||||
return BitConverter.ToUInt16(this.data, baseOffset + offsets[0]);
|
||||
if (offsets.Length == 0) return DefaultValue(key, (ushort)0);
|
||||
return BitConverter.ToUInt16(this.Data, BaseOffset + offsets[0]);
|
||||
}
|
||||
|
||||
public void SetWord(string key, int value)
|
||||
@@ -93,8 +97,8 @@ namespace ChanSort.Api
|
||||
var offsets = settings.GetIntList(key);
|
||||
foreach (int offset in offsets)
|
||||
{
|
||||
this.data[baseOffset + offset + 0] = (byte)value;
|
||||
this.data[baseOffset + offset + 1] = (byte)(value>>8);
|
||||
this.Data[BaseOffset + offset + 0] = (byte)value;
|
||||
this.Data[BaseOffset + offset + 1] = (byte)(value>>8);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -103,8 +107,8 @@ namespace ChanSort.Api
|
||||
public long GetDword(string key)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
if (offsets.Length == 0) return 0;
|
||||
return BitConverter.ToUInt32(this.data, baseOffset + offsets[0]);
|
||||
if (offsets.Length == 0) return DefaultValue(key, (uint)0);
|
||||
return BitConverter.ToUInt32(this.Data, BaseOffset + offsets[0]);
|
||||
}
|
||||
|
||||
public void SetDword(string key, long value)
|
||||
@@ -112,10 +116,10 @@ namespace ChanSort.Api
|
||||
var offsets = settings.GetIntList(key);
|
||||
foreach (int offset in offsets)
|
||||
{
|
||||
this.data[baseOffset + offset + 0] = (byte)value;
|
||||
this.data[baseOffset + offset + 1] = (byte)(value >> 8);
|
||||
this.data[baseOffset + offset + 2] = (byte)(value >> 16);
|
||||
this.data[baseOffset + offset + 3] = (byte)(value >> 24);
|
||||
this.Data[BaseOffset + offset + 0] = (byte)value;
|
||||
this.Data[BaseOffset + offset + 1] = (byte)(value >> 8);
|
||||
this.Data[BaseOffset + offset + 2] = (byte)(value >> 16);
|
||||
this.Data[BaseOffset + offset + 3] = (byte)(value >> 24);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -124,8 +128,8 @@ namespace ChanSort.Api
|
||||
public float GetFloat(string key)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
if (offsets.Length == 0) return 0;
|
||||
return BitConverter.ToSingle(this.data, baseOffset + offsets[0]);
|
||||
if (offsets.Length == 0) return DefaultValue(key, 0f);
|
||||
return BitConverter.ToSingle(this.Data, BaseOffset + offsets[0]);
|
||||
}
|
||||
|
||||
public void SetFloat(string key, float value)
|
||||
@@ -135,25 +139,25 @@ namespace ChanSort.Api
|
||||
foreach (int offset in offsets)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
this.data[baseOffset + offset + i] = bytes[i];
|
||||
this.Data[BaseOffset + offset + i] = bytes[i];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetFlag
|
||||
|
||||
public bool GetFlag(string key, bool defaultValue = false)
|
||||
public bool GetFlag(string key, bool defaultValue)
|
||||
{
|
||||
return GetFlag("off" + key, "mask" + key, defaultValue);
|
||||
}
|
||||
|
||||
public bool GetFlag(string valueKey, string maskKey, bool defaultValue = false)
|
||||
public bool GetFlag(string valueKey, string maskKey, bool defaultValue)
|
||||
{
|
||||
int mask = settings.GetInt(maskKey);
|
||||
return GetFlag(valueKey, mask, defaultValue);
|
||||
}
|
||||
|
||||
public bool GetFlag(string valueKey, int mask, bool defaultValue = false)
|
||||
public bool GetFlag(string valueKey, int mask, bool defaultValue)
|
||||
{
|
||||
if (mask == 0) return defaultValue;
|
||||
|
||||
@@ -165,7 +169,7 @@ namespace ChanSort.Api
|
||||
}
|
||||
var offsets = settings.GetIntList(valueKey);
|
||||
if (offsets.Length == 0) return defaultValue;
|
||||
bool isSet = (this.data[baseOffset + offsets[0]] & mask) == mask;
|
||||
bool isSet = (this.Data[BaseOffset + offsets[0]] & mask) == mask;
|
||||
return isSet != reverseLogic;
|
||||
}
|
||||
#endregion
|
||||
@@ -195,9 +199,9 @@ namespace ChanSort.Api
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
if (value != reverseLogic)
|
||||
this.data[baseOffset + offset] |= (byte)mask;
|
||||
this.Data[BaseOffset + offset] |= (byte)mask;
|
||||
else
|
||||
this.data[baseOffset + offset] &= (byte)~mask;
|
||||
this.Data[BaseOffset + offset] &= (byte)~mask;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -207,12 +211,12 @@ namespace ChanSort.Api
|
||||
public string GetString(string key, int maxLen)
|
||||
{
|
||||
var offsets = settings.GetIntList(key);
|
||||
if (offsets.Length == 0) return null;
|
||||
if (offsets.Length == 0) return DefaultValue(key, (string)null);
|
||||
int length = this.GetByte(key + "Length");
|
||||
if (length == 0)
|
||||
length = maxLen;
|
||||
var encoding = this.DefaultEncoding;
|
||||
return encoding.GetString(this.data, baseOffset + offsets[0], length).TrimEnd('\0');
|
||||
return encoding.GetString(this.Data, BaseOffset + offsets[0], length).TrimEnd('\0');
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -223,13 +227,30 @@ namespace ChanSort.Api
|
||||
int len = Math.Min(bytes.Length, maxLen);
|
||||
foreach (var offset in settings.GetIntList(key))
|
||||
{
|
||||
Array.Copy(bytes, 0, this.data, baseOffset + offset, len);
|
||||
Array.Copy(bytes, 0, this.Data, BaseOffset + offset, len);
|
||||
for (int i = len; i < maxLen; i++)
|
||||
this.data[baseOffset + offset + i] = 0;
|
||||
this.Data[BaseOffset + offset + i] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DefaultValue()
|
||||
private T DefaultValue<T>(string key, T value)
|
||||
{
|
||||
if (this.ThrowWhenAccessingUnknownSetting)
|
||||
throw new ArgumentException($"undefined setting \"{key}\" in {this.Settings}");
|
||||
return value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region HasValue()
|
||||
public bool HasValue(string key)
|
||||
{
|
||||
var list = this.settings.GetIntList(key);
|
||||
return list != null && list.Length > 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ namespace ChanSort.Api
|
||||
{
|
||||
private readonly Dictionary<string, string> data = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
public Section(string name)
|
||||
public Section(string name, IniFile iniFile)
|
||||
{
|
||||
this.Name = name;
|
||||
this.IniFile = iniFile;
|
||||
}
|
||||
|
||||
public IniFile IniFile { get; }
|
||||
|
||||
#region Name
|
||||
public string Name { get; }
|
||||
#endregion
|
||||
@@ -121,24 +124,31 @@ namespace ChanSort.Api
|
||||
return intValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override string ToString() => $"{this.IniFile} [{this.Name}]";
|
||||
}
|
||||
#endregion
|
||||
|
||||
private readonly Dictionary<string, Section> sectionDict;
|
||||
private readonly List<Section> sectionList;
|
||||
private readonly string filePath;
|
||||
|
||||
public IniFile(string fileName)
|
||||
public IniFile(string filePath)
|
||||
{
|
||||
this.filePath = filePath;
|
||||
this.sectionDict = new Dictionary<string, Section>();
|
||||
this.sectionList = new List<Section>();
|
||||
this.ReadIniFile(fileName);
|
||||
this.ReadIniFile(filePath);
|
||||
}
|
||||
|
||||
public IEnumerable<Section> Sections => this.sectionList;
|
||||
|
||||
public Section GetSection(string sectionName)
|
||||
public Section GetSection(string sectionName, bool throwException = false)
|
||||
{
|
||||
return sectionDict.TryGet(sectionName);
|
||||
var sec = sectionDict.TryGet(sectionName);
|
||||
if (sec == null && throwException)
|
||||
throw new ArgumentException($"No [{sectionName}] in {this}");
|
||||
return sec;
|
||||
}
|
||||
|
||||
#region ReadIniFile()
|
||||
@@ -152,14 +162,14 @@ namespace ChanSort.Api
|
||||
while ((line = rdr.ReadLine()) != null)
|
||||
{
|
||||
string trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith(";"))
|
||||
if (trimmedLine.Length == 0 || trimmedLine.StartsWith(";") || trimmedLine.StartsWith("#") || trimmedLine.StartsWith("//"))
|
||||
continue;
|
||||
if (trimmedLine.StartsWith("["))
|
||||
{
|
||||
string sectionName = trimmedLine.EndsWith("]")
|
||||
? trimmedLine.Substring(1, trimmedLine.Length - 2)
|
||||
: trimmedLine.Substring(1);
|
||||
currentSection = new Section(sectionName);
|
||||
currentSection = new Section(sectionName, this);
|
||||
this.sectionList.Add(currentSection);
|
||||
this.sectionDict[sectionName] = currentSection;
|
||||
continue;
|
||||
@@ -186,5 +196,7 @@ namespace ChanSort.Api
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override string ToString() => Path.GetFileName(this.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,10 +160,10 @@ namespace ChanSort.Loader.CmdbBin
|
||||
ch.PcrPid = chanMap.GetWord("offPcrPid") & 0x1FFF;
|
||||
ch.AudioPid = chanMap.GetWord("offAudioPid") & 0x1FFF;
|
||||
ch.VideoPid = chanMap.GetWord("offVideoPid") & 0x1FFF;
|
||||
ch.Encrypted = chanMap.GetFlag("Encrypted");
|
||||
ch.Skip = chanMap.GetFlag("Skip");
|
||||
ch.Lock = chanMap.GetFlag("Locked");
|
||||
ch.Favorites = chanMap.GetFlag("Fav") ? Favorites.A : 0;
|
||||
ch.Encrypted = chanMap.GetFlag("Encrypted", false);
|
||||
ch.Skip = chanMap.GetFlag("Skip", false);
|
||||
ch.Lock = chanMap.GetFlag("Locked", false);
|
||||
ch.Favorites = chanMap.GetFlag("Fav", false) ? Favorites.A : 0;
|
||||
|
||||
var off = chanMap.BaseOffset + chanMap.GetOffsets("offName")[0];
|
||||
this.dvbStringDecoder.GetChannelNames(chanMap.Data, off, chanMap.Settings.GetInt("lenName"), out var longName, out var shortName);
|
||||
|
||||
@@ -51,11 +51,11 @@ namespace ChanSort.Loader.LG.Binary
|
||||
this.ParseNames();
|
||||
|
||||
this.Favorites = (Favorites)((data.GetByte(_Favorites2) & 0x3C) >> 2);
|
||||
this.Lock = data.GetFlag(_Lock);
|
||||
this.Skip = data.GetFlag(_Skip);
|
||||
this.Hidden = data.GetFlag(_Hide);
|
||||
this.Encrypted = data.GetFlag(_Encrypted);
|
||||
this.IsDeleted = data.GetFlag(_Deleted);
|
||||
this.Lock = data.GetFlag(_Lock, false);
|
||||
this.Skip = data.GetFlag(_Skip, false);
|
||||
this.Hidden = data.GetFlag(_Hide, false);
|
||||
this.Encrypted = data.GetFlag(_Encrypted, false);
|
||||
this.IsDeleted = data.GetFlag(_Deleted, false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ from channel c inner join chanseq s on s.listid=c.listid and s.slot=c.slot
|
||||
cmd.Parameters["@listid"].Value = listId;
|
||||
cmd.Parameters["@slot"].Value = slot;
|
||||
cmd.Parameters["@seq"].Value = DBNull.Value;
|
||||
cmd.Parameters["@isdel"].Value = dvbsMapping.GetFlag("InUse") ? 0 : 1;
|
||||
cmd.Parameters["@isdel"].Value = dvbsMapping.GetFlag("InUse", false) ? 0 : 1;
|
||||
cmd.Parameters["@progmask"].Value = dvbsMapping.GetWord("offProgramNr");
|
||||
cmd.Parameters["@prognr"].Value = dvbsMapping.GetWord("offProgramNr") & 0x3FFF;
|
||||
cmd.Parameters["@progfix"].Value = dvbsMapping.GetWord("offProgramNrPreset");
|
||||
|
||||
@@ -492,12 +492,12 @@ namespace ChanSort.Loader.Philips
|
||||
|
||||
// onid, tsid, pcrpid and vpid can be 0 in some lists
|
||||
ch.PcrPid = mapping.GetWord("offPcrPid") & mapping.GetMask("maskPcrPid");
|
||||
ch.Lock = mapping.GetFlag("Locked");
|
||||
ch.Lock = mapping.GetFlag("Locked", false);
|
||||
ch.OriginalNetworkId = mapping.GetWord("offOnid");
|
||||
ch.TransportStreamId = mapping.GetWord("offTsid");
|
||||
ch.ServiceId = mapping.GetWord("offSid");
|
||||
ch.VideoPid = mapping.GetWord("offVpid") & mapping.GetMask("maskVpid");
|
||||
ch.Favorites = mapping.GetFlag("IsFav") ? Favorites.A : 0;
|
||||
ch.Favorites = mapping.GetFlag("IsFav", false) ? Favorites.A : 0;
|
||||
ch.OldProgramNr = progNr;
|
||||
ch.Id = mapping.GetWord("offId"); // relevant for ChannelMap45
|
||||
|
||||
|
||||
@@ -2,22 +2,31 @@
|
||||
# Repair\CM_*_LA_CK.BIN file (or similar) with a hidden .xml - stable
|
||||
|
||||
[Repair_xml]
|
||||
reorderRecordsByChannelNumber=false
|
||||
; nothing to configure
|
||||
|
||||
|
||||
############################################################################
|
||||
# Repair\channel_db_ver.db, atv_chan_phy_c.db, FLASH_DTVINFO_S_FTA, mgr_chan_s_fta.db, ... - experimental
|
||||
# unfortunately read-only because all attempts so far produced inconsistencies between the channels in the menu and what the tuner actually put on screen
|
||||
# There is a program number at offset 0 and another one around 450. My guess is that the 1st is the "new" number and the 2nd the "old" number - or it is the other way around.
|
||||
# Repair\channel_db_ver.db, mgr_chan_s_fta.db, FLASH_DTVINFO_S_FTA, _pkg, ... - experimental
|
||||
# There are different version of this format using different offsets/lengths/sizes.
|
||||
# The calculated length of a channel record in the .db file is used to select specific config sections.
|
||||
#
|
||||
# There is a program number at offset 0 and another one around 450 in the .db file.
|
||||
# My guess is that the 1st is the "new" number and the 2nd the "old" number - or it is the other way around.
|
||||
# When both numbers are updated and also the value of RecordIndex is set to PrNr-1, the TV doesn't show any changes.
|
||||
|
||||
; ---------------------------------------------
|
||||
; mappings that are the same for all format variants
|
||||
|
||||
[mgr_chan_s_fta.db]
|
||||
lenHeader=64
|
||||
lenEntry=476
|
||||
lenFooter=12
|
||||
offFooterChecksum=8
|
||||
reorderRecordsByChannelNumber=false
|
||||
allowEdit=true
|
||||
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
|
||||
@@ -32,15 +41,16 @@ offTsid=460
|
||||
offSid=464
|
||||
offOnid=466
|
||||
|
||||
|
||||
|
||||
[mgr_chan_s_pkg.db]
|
||||
lenHeader=64
|
||||
lenEntry=480
|
||||
lenFooter=12
|
||||
offFooterChecksum=8
|
||||
reorderRecordsByChannelNumber=false
|
||||
allowEdit=true
|
||||
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
|
||||
@@ -55,15 +65,17 @@ offTsid=460
|
||||
offSid=464
|
||||
offOnid=466
|
||||
|
||||
|
||||
|
||||
[mgr_chan_dvbt.db]
|
||||
lenHeader=64
|
||||
lenEntry=472
|
||||
lenFooter=12
|
||||
offFooterChecksum=8
|
||||
reorderRecordsByChannelNumber=false
|
||||
allowEdit=true
|
||||
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
|
||||
@@ -80,15 +92,17 @@ offSymbolRate=462
|
||||
offSid=464
|
||||
offOnid=466
|
||||
|
||||
|
||||
|
||||
[mgr_chan_dvbc.db]
|
||||
lenHeader=64
|
||||
lenEntry=472
|
||||
lenFooter=12
|
||||
offFooterChecksum=8
|
||||
reorderRecordsByChannelNumber=false
|
||||
allowEdit=true
|
||||
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]
|
||||
offProgNr=0
|
||||
@@ -105,7 +119,130 @@ offSymbolRate=462
|
||||
offSid=464
|
||||
offOnid=466
|
||||
|
||||
; ---------------------------------------------
|
||||
; variant with 476 bytes per entry in mgr_chan_s_fta.db
|
||||
|
||||
[flash_dtvinfo_s_fta:476]
|
||||
offChannelTransponderTable=4
|
||||
numChannelTransponderTable=4000
|
||||
lenChannelTransponder=4
|
||||
offTransponderTable=0x5f2c
|
||||
numTransponderTable=1100
|
||||
lenTransponder=31
|
||||
offSatellites=0xe460
|
||||
offChannelBlock=0x10000
|
||||
lenChannelBlock=0x10000
|
||||
; struct Transponder
|
||||
transponderId=0
|
||||
freq=4,21
|
||||
symbolRate=6
|
||||
onid=10
|
||||
tsid=12
|
||||
nid=14
|
||||
; struct ChannelBlock
|
||||
offChannel=4
|
||||
numChannel=734
|
||||
lenChannel=89
|
||||
; struct Channel
|
||||
channelId=0
|
||||
flags=3
|
||||
sid=8
|
||||
pcrPid=10
|
||||
vpid=12
|
||||
progNr=22
|
||||
apid=31
|
||||
|
||||
[flash_dtvinfo_s_pkg:476]
|
||||
offChannelTransponderTable=4
|
||||
numChannelTransponderTable=2000
|
||||
lenChannelTransponder=4
|
||||
offTransponderTable=0x287C
|
||||
numTransponderTable=550
|
||||
lenTransponder=31
|
||||
offSatellites=0x6B16
|
||||
offChannelBlock=0x10000
|
||||
lenChannelBlock=0x10000
|
||||
numChannel=734
|
||||
; struct transponder
|
||||
transponderId=0
|
||||
freq=4,21
|
||||
symbolRate=6
|
||||
onid=10
|
||||
tsid=12
|
||||
; relative to ChannelBlock
|
||||
offChannel=4
|
||||
lenChannel=89
|
||||
; channels within the ChannelBlock
|
||||
channelId=0
|
||||
flags=3
|
||||
sid=8
|
||||
pcrPid=10
|
||||
vpid=12
|
||||
progNr=22
|
||||
apid=31
|
||||
|
||||
;-------------------------------------------------------------------------
|
||||
; variant with 480 bytes per entry in mgr_chan_s_fta.db
|
||||
|
||||
[flash_dtvinfo_s_fta:480]
|
||||
offChannelTransponderTable=4
|
||||
numChannelTransponderTable=4000
|
||||
lenChannelTransponder=4
|
||||
offTransponderTable=0x4f14
|
||||
numTransponderTable=1100
|
||||
lenTransponder=36
|
||||
offSatellites=0xe9c4
|
||||
offChannelBlock=0x10000
|
||||
lenChannelBlock=0x10000
|
||||
reorderRecordsByChannelNumber=false
|
||||
; struct Transponder
|
||||
transponderId=0
|
||||
freq=4,21
|
||||
symbolRate=6
|
||||
onid=10
|
||||
tsid=12
|
||||
nid=14
|
||||
; struct ChannelBlock
|
||||
offChannel=4
|
||||
numChannel=703
|
||||
lenChannel=93
|
||||
; struct Channel
|
||||
channelId=0
|
||||
flags=3
|
||||
sid=8
|
||||
pcrPid=10
|
||||
vpid=12
|
||||
progNr=24
|
||||
apid=34
|
||||
|
||||
[flash_dtvinfo_s_pkg:480]
|
||||
offChannelTransponderTable=4
|
||||
numChannelTransponderTable=2000
|
||||
lenChannelTransponder=4
|
||||
offTransponderTable=0x2804
|
||||
numTransponderTable=550
|
||||
lenTransponder=36
|
||||
offSatellites=0x755C
|
||||
offChannelBlock=0x10000
|
||||
lenChannelBlock=0x10000
|
||||
; struct transponder
|
||||
transponderId=0
|
||||
freq=4,21
|
||||
symbolRate=6
|
||||
onid=10
|
||||
tsid=12
|
||||
; struct ChannelBlock
|
||||
offChannel=4
|
||||
numChannel=703
|
||||
lenChannel=93
|
||||
; struct Channel
|
||||
channelId=0
|
||||
flags=3
|
||||
sid=8
|
||||
pcrPid=10
|
||||
vpid=12
|
||||
progNr=24
|
||||
apid=34
|
||||
|
||||
############################################################################
|
||||
# Repair\ChannelList\chanLst.bin version 1.x with AntennaDigSrvTable, CableDigSrvTable and service.dat (for DVB-S) - should be stable
|
||||
|
||||
@@ -36,5 +36,11 @@ namespace ChanSort.Loader.Philips
|
||||
/// </summary>
|
||||
public int Id; // links entries in the ChannelMap45/*Db.bin files with the entries in the tv.db channels table
|
||||
|
||||
/// <summary>
|
||||
/// used in the mgr_db / FLASH format to hold the absolute offset of the channel in the FLASH file
|
||||
/// </summary>
|
||||
public int FlashFileOffset;
|
||||
|
||||
public int DbFileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,27 @@ using ChanSort.Api;
|
||||
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.
|
||||
* Due to lack of sample lists, the analog and DVB-C files have not been reverse engineered yet.
|
||||
* The data offsets are defined in ChanSort.Loader.Philips.ini
|
||||
*/
|
||||
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, FLASH_DTVINFO_S_FTA, ...
|
||||
The .db files are proprietary binary files, not SQLite databases.
|
||||
|
||||
There are several variations of this file format using different record sizes, record counts and data offsets. Currently 2 different formats are known.
|
||||
They are identified by their channel record size in the mgr_chan_s_fta.db file, currently supporting 476 and 480. This decides the config that will be used for other files too.
|
||||
|
||||
Lots of data is duplicated between FLASH_* and *.db files and must be updated in both.
|
||||
It seems that the .db file contains valid channels and the index of a channel record in this file is used as an index in the ChannelIdMappingTable of the FLASH file
|
||||
where IDs for for the channel and transponder are stored, which are used to find channels and transponders in the FLASH file.
|
||||
The data records in the FLASH file are then looked up by their IDs, not by index.
|
||||
|
||||
A full satellite scan usually populates the mgr_chan_s_fta.db + FLASH_DTVINFO_S_FTA files. A preset list fills mgr_chan_s_pkg.db and FLASH_DTVINFO_S_PKG.
|
||||
However there is also an example where a preset list uses mgr_chan_s_pkg.db + FLASH_DTVINFO_S_FTA.
|
||||
|
||||
A preset list has a .db file where records are ordered by the desired channel order. The corresponding FLASH file however has a different channel record order
|
||||
and the ID-mapping table is used to resolve references.
|
||||
|
||||
Due to lack of sample lists, the analog and DVB-T files have not been reverse engineered yet, but DVB-T is supported experimentally assuming it is identical to DVB-C
|
||||
|
||||
The data offsets are defined in ChanSort.Loader.Philips.ini
|
||||
*/
|
||||
class DbSerializer : SerializerBase
|
||||
{
|
||||
private readonly IniFile ini;
|
||||
@@ -22,9 +38,15 @@ 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> fileByList = new();
|
||||
private readonly Dictionary<ChannelList, string> dbFileByList = new();
|
||||
private readonly Dictionary<ChannelList, Tuple<string, int>> flashFileByList = new();
|
||||
private int dvbtChannelRecordLength;
|
||||
private int dvbcChannelRecordLength;
|
||||
private int ftaChannelRecordLength;
|
||||
private int pkgChannelRecordLength;
|
||||
|
||||
|
||||
#region ctor()
|
||||
public DbSerializer(string inputFile) : base(inputFile)
|
||||
{
|
||||
this.Features.MaxFavoriteLists = 1;
|
||||
@@ -43,28 +65,39 @@ namespace ChanSort.Loader.Philips
|
||||
{
|
||||
list.VisibleColumnFieldNames = new List<string>
|
||||
{
|
||||
"Position", //nameof(Channel.NewProgramNr),
|
||||
"OldPosition", // nameof(Channel.OldProgramNr),
|
||||
// data in *.db files
|
||||
"Position", // new progNr in main or fav list
|
||||
"OldPosition", // old progNr in main or fav list,
|
||||
nameof(Channel.Name),
|
||||
nameof(Channel.Favorites),
|
||||
nameof(Channel.FreqInMhz),
|
||||
nameof(Channel.SymbolRate),
|
||||
nameof(Channel.TransportStreamId),
|
||||
nameof(Channel.OriginalNetworkId),
|
||||
nameof(Channel.ServiceId)
|
||||
nameof(Channel.ServiceId),
|
||||
|
||||
// additional data in FLASH_ files only
|
||||
nameof(ChannelInfo.PcrPid),
|
||||
nameof(ChannelInfo.VideoPid),
|
||||
nameof(ChannelInfo.AudioPid),
|
||||
nameof(ChannelInfo.ServiceTypeName)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Load()
|
||||
public override void Load()
|
||||
{
|
||||
bool validList = false;
|
||||
|
||||
foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName) ?? ""))
|
||||
var dir = Path.GetDirectoryName(this.FileName) ?? ".";
|
||||
// must process *.db files first, then the FLASH_ files
|
||||
var files = Directory.GetFiles(dir, "*.db").Union(Directory.GetFiles(dir, "FLASH_*"));
|
||||
foreach (var file in files)
|
||||
{
|
||||
var lc = Path.GetFileName(file).ToLowerInvariant();
|
||||
switch (lc)
|
||||
var lowercaseFileName = Path.GetFileName(file).ToLowerInvariant();
|
||||
switch (lowercaseFileName)
|
||||
{
|
||||
case "atv_channel_t.db":
|
||||
// TODO: no sample file yet that contains analog terrestrial channels
|
||||
@@ -76,27 +109,47 @@ namespace ChanSort.Loader.Philips
|
||||
LoadVersion(file);
|
||||
break;
|
||||
case "mgr_chan_dvbt.db":
|
||||
LoadDvb(file, lc, dvbtChannels);
|
||||
LoadDvb(file, lowercaseFileName, dvbtChannels, ref dvbtChannelRecordLength);
|
||||
validList = true;
|
||||
break;
|
||||
case "mgr_chan_dvbc.db":
|
||||
// no sample file with DVB-C data yet, so this here is a guess based on DVB-T
|
||||
LoadDvb(file, lc, dvbcChannels);
|
||||
LoadDvb(file, lowercaseFileName, dvbcChannels, ref dvbcChannelRecordLength);
|
||||
validList = true;
|
||||
break;
|
||||
case "mgr_chan_s_fta.db":
|
||||
LoadDvb(file, lc, dvbsFtaChannels);
|
||||
LoadDvb(file, lowercaseFileName, dvbsFtaChannels, ref ftaChannelRecordLength);
|
||||
validList = true;
|
||||
break;
|
||||
case "mgr_chan_s_pkg.db":
|
||||
LoadDvb(file, lc, dvbsPkgChannels);
|
||||
LoadDvb(file, lowercaseFileName, dvbsPkgChannels, ref pkgChannelRecordLength);
|
||||
validList = true;
|
||||
break;
|
||||
case "flash_dtvinfo_s_fta":
|
||||
if (dvbsFtaChannels.Count == 0 && dvbsPkgChannels.Count > 0)
|
||||
LoadFlash(file, lowercaseFileName, dvbsPkgChannels, pkgChannelRecordLength); // weird case where _pkg.db must be combined with FLASH_FTA
|
||||
else
|
||||
LoadFlash(file, lowercaseFileName, dvbsFtaChannels, ftaChannelRecordLength);
|
||||
break;
|
||||
case "flash_dtvinfo_s_pkg":
|
||||
if (!(dvbsFtaChannels.Count == 0 && dvbsPkgChannels.Count > 0))
|
||||
LoadFlash(file, lowercaseFileName, dvbsPkgChannels, pkgChannelRecordLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validList)
|
||||
throw new FileLoadException(this.FileName + " is not a supported Philips Repair/channel_db_ver.db channel list");
|
||||
|
||||
foreach (var channelList in this.DataRoot.ChannelLists)
|
||||
{
|
||||
foreach (var channelInfo in channelList.Channels)
|
||||
{
|
||||
var ch = (Channel)channelInfo;
|
||||
if (ch.FlashFileOffset == 0)
|
||||
this.DataRoot.Warnings.AppendLine($"Channel with index {ch.RecordIndex:d4} in .db file ({ch.OldProgramNr} - {ch.Name}) has no entry in FLASH files");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -132,22 +185,26 @@ namespace ChanSort.Loader.Philips
|
||||
#endregion
|
||||
|
||||
#region LoadDvbs()
|
||||
private void LoadDvb(string path, string sectionName, ChannelList list)
|
||||
private void LoadDvb(string path, string sectionName, ChannelList list, ref int channelRecordLength)
|
||||
{
|
||||
var signalSource = list.SignalSource;
|
||||
var data = File.ReadAllBytes(path);
|
||||
|
||||
var sec = ini.GetSection(sectionName);
|
||||
var lenHeader = sec.GetInt("lenHeader");
|
||||
var lenFooter = sec.GetInt("lenFooter");
|
||||
var lenEntry = sec.GetInt("lenEntry");
|
||||
var offFooterChecksum = sec.GetInt("offFooterChecksum");
|
||||
|
||||
var records = (data.Length - lenHeader - lenFooter) / lenEntry;
|
||||
if (records <= 0)
|
||||
if (!GetValuesFromDvbFileHeader(sec, data, out var lenHeader, out var lenEntry, out var records, out var offChecksum))
|
||||
{
|
||||
this.DataRoot.Warnings.AppendLine($"{sectionName} was not loaded because data record size could not be determined");
|
||||
return;
|
||||
}
|
||||
|
||||
list.ReadOnly = !sec.GetBool("allowEdit", false);
|
||||
var expectedChecksum = BitConverter.ToUInt16(data, offChecksum);
|
||||
var actualChecksum = (UInt16)CalcChecksum(data, 0, offChecksum);
|
||||
if (actualChecksum != expectedChecksum)
|
||||
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");
|
||||
@@ -157,13 +214,13 @@ namespace ChanSort.Loader.Philips
|
||||
mapping.SetDataPtr(data, lenHeader + i * lenEntry);
|
||||
var oldProgNr = mapping.GetWord("offProgNr");
|
||||
|
||||
// name can be either an 8-bit ASCII with unspecified encoding or big-endian 16-bit unicode
|
||||
// name is normally in 8-bit ASCII with unspecified encoding, but there has been an instance where some names in the file were in 16 bit big-endian unicode
|
||||
var off = mapping.BaseOffset + mapping.GetOffsets("offName")[0];
|
||||
var name = data[off + 0] == 0 ? (data[off + 1] == 0 ? "" : Encoding.BigEndianUnicode.GetString(data, off, lenName)) : DefaultEncoding.GetString(data, off, lenName);
|
||||
name = name.TrimEnd('\0');
|
||||
|
||||
var ch = new Channel(signalSource, i, oldProgNr, name);
|
||||
ch.RecordOrder = i;
|
||||
ch.DbFileOffset = mapping.BaseOffset;
|
||||
var favPos = mapping.GetWord("offFav");
|
||||
if (favPos > 0)
|
||||
ch.SetOldPosition(1, favPos);
|
||||
@@ -179,24 +236,219 @@ namespace ChanSort.Loader.Philips
|
||||
this.DataRoot.AddChannel(list, ch);
|
||||
}
|
||||
|
||||
var offChecksum = data.Length - lenFooter + offFooterChecksum;
|
||||
var expectedChecksum = BitConverter.ToUInt16(data, offChecksum);
|
||||
var actualChecksum = CalcChecksum(data, 0, offChecksum);
|
||||
if (actualChecksum != expectedChecksum)
|
||||
throw new FileLoadException($"File {path} contains invalid checksum. Expected {expectedChecksum:x4} but calculated {actualChecksum:x4}");
|
||||
|
||||
this.fileByList[list] = path;
|
||||
this.dbFileByList[list] = path;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetValuesFromDvbFileHeader()
|
||||
private bool GetValuesFromDvbFileHeader(IniFile.Section sec, byte[] data, out int lenHeader, out int lenEntry, out int numEntries, out int checksumOffset)
|
||||
{
|
||||
lenHeader = 0;
|
||||
lenEntry = 0;
|
||||
numEntries = 0;
|
||||
checksumOffset = -1;
|
||||
|
||||
// _fta.db or _pkg.db may contain only a tiny header without the required header fields or actual data
|
||||
if (data.Length < sec.GetInt("channelBlockSize") + 4)
|
||||
return false;
|
||||
|
||||
lenHeader = sec.GetInt("lenHeader");
|
||||
var lenFooter = sec.GetInt("lenFooter");
|
||||
|
||||
// the size of the channel entry can vary, so it must be calculated (seen 476 and 480 so far in mgr_chan_s_pkg.db and 472 in mgr_chan_dvbt.db)
|
||||
var mapping = new DataMapping(sec, data);
|
||||
var blockSize = mapping.GetDword("channelBlockSize");
|
||||
numEntries = mapping.GetWord("numTvChannels") + mapping.GetWord("numRadioChannels");
|
||||
if (numEntries == 0)
|
||||
{
|
||||
lenEntry = sec.GetInt("lenEntry");
|
||||
if (lenEntry == 0)
|
||||
return false;
|
||||
numEntries = (int)(blockSize / lenEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
lenEntry = (int)(blockSize / numEntries);
|
||||
}
|
||||
|
||||
if (blockSize % numEntries != 0)
|
||||
return false;
|
||||
|
||||
|
||||
var offFooterChecksum = sec.GetInt("offFooterChecksum");
|
||||
checksumOffset = data.Length - lenFooter + offFooterChecksum;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LoadFlash()
|
||||
private void LoadFlash(string path, string sectionName, ChannelList channelList, int dbChannelRecordLength)
|
||||
{
|
||||
if (dbChannelRecordLength == 0) // if the .db file wasn't read, ignore the FLASH file
|
||||
return;
|
||||
var data = File.ReadAllBytes(path);
|
||||
if (data.Length < 4)
|
||||
return;
|
||||
|
||||
var expectedChecksum = BitConverter.ToUInt32(data, data.Length - 4);
|
||||
var actualChecksum = CalcChecksum(data, 0, data.Length - 4);
|
||||
if (actualChecksum != expectedChecksum)
|
||||
throw new FileLoadException($"File {path} contains invalid checksum. Expected {expectedChecksum:x8} but calculated {actualChecksum:x8}");
|
||||
|
||||
var settings = this.ini.GetSection(sectionName + ":" + dbChannelRecordLength, true);
|
||||
var mapping = new DataMapping(settings, data);
|
||||
|
||||
ReadTransponderFromFlash(settings, mapping);
|
||||
var idMapping = ReadChannelIdMappingFromFlash(settings, data);
|
||||
var filename = Path.GetFileName(path);
|
||||
ReadChannelBlocksFromFlash(filename, channelList, settings, data, mapping, idMapping);
|
||||
|
||||
this.flashFileByList[channelList] = Tuple.Create(path, dbChannelRecordLength);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadTransponderFromFlash()
|
||||
private void ReadTransponderFromFlash(IniFile.Section settings, DataMapping mapping)
|
||||
{
|
||||
// read transponders (mostly for validating data that was already read from .db file)
|
||||
var off = settings.GetInt("offTransponderTable");
|
||||
var num = settings.GetInt("numTransponderTable");
|
||||
var len = settings.GetInt("lenTransponder");
|
||||
mapping.BaseOffset = off;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
var id = mapping.GetWord("transponderId");
|
||||
if (id != 0xFFFF)
|
||||
{
|
||||
var tp = new Transponder(id);
|
||||
tp.FrequencyInMhz = mapping.GetWord("freq");
|
||||
tp.SymbolRate = mapping.GetWord("symbolRate");
|
||||
tp.OriginalNetworkId = mapping.GetWord("onid");
|
||||
tp.TransportStreamId = mapping.GetWord("tsid");
|
||||
this.DataRoot.AddTransponder(null, tp);
|
||||
}
|
||||
|
||||
mapping.BaseOffset += len;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadChannelIdMappingFromFlash()
|
||||
private static Dictionary<int, IndexToIdMapping> ReadChannelIdMappingFromFlash(IniFile.Section settings, byte[] data)
|
||||
{
|
||||
// read a table that maps the channel index from the .db file to a channel ID and transponder ID in the FLASH file
|
||||
// and convert this into a reverse-lookup that allows to map a channel ID to a channel index and transponder ID
|
||||
var idMapping = new Dictionary<int, IndexToIdMapping>();
|
||||
var off = settings.GetInt("offChannelTransponderTable");
|
||||
var num = settings.GetInt("numChannelTransponderTable");
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
var chanId = BitConverter.ToUInt16(data, off + i * 4 + 2);
|
||||
if (chanId == 0xFFFF) continue;
|
||||
var transpId = BitConverter.ToUInt16(data, off + i * 4 + 0);
|
||||
var flags = transpId & 0x1F;
|
||||
transpId >>= 5;
|
||||
idMapping[chanId] = new IndexToIdMapping(i, transpId, flags);
|
||||
}
|
||||
return idMapping;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadChannelBlocksFromFlash()
|
||||
private void ReadChannelBlocksFromFlash(string filename, ChannelList channelList, IniFile.Section settings, byte[] data, DataMapping mapping, Dictionary<int,IndexToIdMapping> idMapping)
|
||||
{
|
||||
// channel data is spread across multiple 64KB data blocks which all have a small header, then 734 channel records and a footer
|
||||
|
||||
var off = settings.GetInt("offChannelBlock");
|
||||
var len = settings.GetInt("lenChannelBlock");
|
||||
for (int block = 0; off + (block + 1) * len <= data.Length; block++)
|
||||
{
|
||||
mapping.BaseOffset = off + block * len + settings.GetInt("offChannel");
|
||||
ReadChannelsFromFlashChannelBlock(filename, channelList, mapping, idMapping, block);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadChannelsFromFlashChannelBlock
|
||||
private void ReadChannelsFromFlashChannelBlock(string filename, ChannelList channelList, DataMapping mapping, Dictionary<int,IndexToIdMapping> channelIdMapping, int block)
|
||||
{
|
||||
var settings = mapping.Settings;
|
||||
var numChannelsInBlock = settings.GetInt("numChannel");
|
||||
var lenChannel = settings.GetInt("lenChannel");
|
||||
|
||||
mapping.BaseOffset -= lenChannel;
|
||||
for (int i = 0; i < numChannelsInBlock; i++)
|
||||
{
|
||||
mapping.BaseOffset += lenChannel;
|
||||
|
||||
var id = mapping.GetWord("channelId");
|
||||
var flags = mapping.GetByte("flags"); // in the sample files this is always 63 except for "dead" records, where it is 0
|
||||
if (id == 0xFFFF || flags == 0)
|
||||
continue;
|
||||
if (!channelIdMapping.TryGetValue(id, out var idMapping))
|
||||
{
|
||||
this.DataRoot.Warnings.AppendLine($"Channel record in {filename}, block {block}, index {i:d4} has no entry in the ID mapping table");
|
||||
continue;
|
||||
}
|
||||
|
||||
var index = idMapping.ChannelIndex;
|
||||
|
||||
if (idMapping.ChannelIndex >= channelList.Channels.Count)
|
||||
{
|
||||
this.DataRoot.Warnings.AppendLine($"Channel record in {filename}, block {block}, index {i:d4} refers to non-existing channel index {index} in the .db file");
|
||||
continue;
|
||||
}
|
||||
|
||||
var ch = (Channel)channelList.Channels[idMapping.ChannelIndex];
|
||||
ch.FlashFileOffset = mapping.BaseOffset;
|
||||
var hasDiff = false;
|
||||
var sid = mapping.GetWord("sid");
|
||||
var progNr = (mapping.GetWord("progNr") & 0x3FFF);
|
||||
hasDiff |= ch.ServiceId != sid;
|
||||
ch.PcrPid = mapping.GetWord("pcrPid");
|
||||
ch.VideoPid = mapping.GetWord("vpid");
|
||||
ch.AudioPid = mapping.GetWord("apid");
|
||||
hasDiff |= ch.OldProgramNr != progNr;
|
||||
|
||||
var isRadio = (idMapping.Flags & 0x08) != 0;
|
||||
if (isRadio)
|
||||
{
|
||||
ch.SignalSource |= SignalSource.Radio;
|
||||
ch.ServiceTypeName = "Radio";
|
||||
}
|
||||
else
|
||||
{
|
||||
ch.SignalSource |= SignalSource.Tv;
|
||||
ch.ServiceTypeName = "TV";
|
||||
}
|
||||
|
||||
if (!this.DataRoot.Transponder.TryGetValue(idMapping.TransponderId, out var tp))
|
||||
this.DataRoot.Warnings.AppendLine($"Channel record in {filename}, block {block}, index {i:d4}: could not find transponder record with id {idMapping.TransponderId}");
|
||||
else
|
||||
{
|
||||
hasDiff |= ch.OriginalNetworkId != tp.OriginalNetworkId;
|
||||
hasDiff |= ch.TransportStreamId != tp.TransportStreamId;
|
||||
hasDiff |= ch.FreqInMhz != tp.FrequencyInMhz;
|
||||
}
|
||||
|
||||
if (hasDiff)
|
||||
this.DataRoot.Warnings.AppendLine($"Channel record in {filename}, block {block}, index {i:d4} does not match data in .db file: " +
|
||||
$"ProgNr={progNr}|{ch.OldProgramNr}, onid={tp?.OriginalNetworkId}|{ch.OriginalNetworkId}, tsid={tp?.TransportStreamId}|{ch.TransportStreamId}, " +
|
||||
$"sid={sid}|{ch.ServiceId}, freq={tp?.FrequencyInMhz}|{ch.FreqInMhz}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region CalcChecksum()
|
||||
|
||||
/// <summary>
|
||||
/// The checksum is the 16-bit sum over the byte-values in the file data from offset 0 to right before the checksum field
|
||||
/// The checksum is the 32-bit sum over the byte-values in the file data from offset 0 to right before the checksum field
|
||||
/// </summary>
|
||||
private ushort CalcChecksum(byte[] data, int start, int len)
|
||||
private uint CalcChecksum(byte[] data, int start, int len)
|
||||
{
|
||||
ushort checksum = 0;
|
||||
uint checksum = 0;
|
||||
while (len > 0)
|
||||
{
|
||||
checksum += data[start++];
|
||||
@@ -207,76 +459,109 @@ namespace ChanSort.Loader.Philips
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override IEnumerable<string> GetDataFilePaths() => this.fileByList.Values.ToList();
|
||||
public override IEnumerable<string> GetDataFilePaths() => this.dbFileByList.Values.Union(this.flashFileByList.Values.Select(tup => tup.Item1));
|
||||
|
||||
#region Save()
|
||||
|
||||
public override void Save(string tvOutputFile)
|
||||
{
|
||||
foreach (var listAndFile in this.fileByList)
|
||||
// update *.db files
|
||||
foreach (var listAndFile in this.dbFileByList)
|
||||
{
|
||||
var list = listAndFile.Key;
|
||||
var file = listAndFile.Value;
|
||||
var secName = Path.GetFileName(file).ToLowerInvariant();
|
||||
SaveDvb(file, secName, list);
|
||||
}
|
||||
}
|
||||
|
||||
// update FLASH_* files
|
||||
foreach (var listAndFile in this.flashFileByList)
|
||||
{
|
||||
var list = listAndFile.Key;
|
||||
var file = listAndFile.Value.Item1;
|
||||
var dbChannelRecordSize = listAndFile.Value.Item2;
|
||||
var secName = Path.GetFileName(file).ToLowerInvariant();
|
||||
SaveFlash(file, secName, list, dbChannelRecordSize);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SaveDvb()
|
||||
private void SaveDvb(string file, string secName, ChannelList list)
|
||||
{
|
||||
var data = File.ReadAllBytes(file);
|
||||
|
||||
var sec = ini.GetSection(secName);
|
||||
var lenHeader = sec.GetInt("lenHeader");
|
||||
var lenFooter = sec.GetInt("lenFooter");
|
||||
var lenEntry = sec.GetInt("lenEntry");
|
||||
var offFooterChecksum = sec.GetInt("offFooterChecksum");
|
||||
if (!GetValuesFromDvbFileHeader(sec, data, out var lenHeader, out var lenEntry, out _, out var offChecksum))
|
||||
return;
|
||||
|
||||
var mapping = new DataMapping(ini.GetSection(secName + "_entry"));
|
||||
|
||||
if (sec.GetBool("reorderRecordsByChannelNumber"))
|
||||
foreach (var chan in list.Channels)
|
||||
{
|
||||
// physically reorder channels
|
||||
var newData = new byte[data.Length];
|
||||
Array.Copy(data, newData, lenHeader);
|
||||
var off = lenHeader + lenEntry * list.Channels.Count;
|
||||
Array.Copy(data, off, newData, off, lenFooter);
|
||||
|
||||
int i = 0;
|
||||
foreach (var ch in list.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;
|
||||
}
|
||||
else
|
||||
{
|
||||
// update channel data
|
||||
foreach (var ch in list.Channels)
|
||||
{
|
||||
mapping.SetDataPtr(data, lenHeader + ch.RecordOrder * lenEntry);
|
||||
mapping.SetWord("offProgNr", ch.NewProgramNr);
|
||||
//mapping.SetWord("offPrevProgNr", ch.NewProgramNr - 1);
|
||||
mapping.SetWord("offFav", Math.Max(0, ch.GetPosition(1)));
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
// update checksum
|
||||
var offChecksum = data.Length - lenFooter + offFooterChecksum;
|
||||
// update checksum (only 16 bits are stored)
|
||||
var checksum = CalcChecksum(data, 0, offChecksum);
|
||||
data[offChecksum] = (byte)checksum;
|
||||
data[offChecksum + 0] = (byte)checksum;
|
||||
data[offChecksum + 1] = (byte)(checksum >> 8);
|
||||
|
||||
File.WriteAllBytes(file, data);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SaveFlash()
|
||||
private void SaveFlash(string file, string secName, ChannelList list, int dbChannelRecordLength)
|
||||
{
|
||||
var data = File.ReadAllBytes(file);
|
||||
if (data.Length == 0)
|
||||
return;
|
||||
|
||||
var sec = ini.GetSection(secName + ":" + dbChannelRecordLength, true);
|
||||
var mapping = new DataMapping(sec, data);
|
||||
|
||||
// in-place update of channel data
|
||||
foreach (var chan in list.Channels)
|
||||
{
|
||||
if (chan is not Channel ch)
|
||||
continue; // skip proxy channels
|
||||
if (ch.FlashFileOffset == 0)
|
||||
continue;
|
||||
mapping.BaseOffset = ch.FlashFileOffset;
|
||||
mapping.SetWord("progNr", ch.NewProgramNr | (mapping.GetWord("progNr") & ~0x3FFF));
|
||||
//mapping.SetWord("fav", Math.Max(0, ch.GetPosition(1)));
|
||||
}
|
||||
|
||||
// update checksum (full 32 bit)
|
||||
var offChecksum = data.Length - 4;
|
||||
var checksum = CalcChecksum(data, 0, offChecksum);
|
||||
data[offChecksum + 0] = (byte)checksum;
|
||||
data[offChecksum + 1] = (byte)(checksum >> 8);
|
||||
data[offChecksum + 2] = (byte)(checksum >> 16);
|
||||
data[offChecksum + 3] = (byte)(checksum >> 24);
|
||||
|
||||
File.WriteAllBytes(file, data);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region IndexToIdMapping
|
||||
class IndexToIdMapping
|
||||
{
|
||||
public readonly int ChannelIndex;
|
||||
public readonly int TransponderId;
|
||||
public readonly int Flags;
|
||||
|
||||
public IndexToIdMapping(int channelIndex, int transponderId, int flags)
|
||||
{
|
||||
this.ChannelIndex = channelIndex;
|
||||
this.TransponderId = transponderId;
|
||||
this.Flags = flags;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace ChanSort.Loader.Philips
|
||||
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.
|
||||
The channel nodes in the .XML must be kept in the original order with "oldpresetnumber" keeping the original value and only "presetnumber" being updated.
|
||||
|
||||
<Channel>
|
||||
<Setup oldpresetnumber="1" presetnumber="1" name="Das Erste" ></Setup>
|
||||
@@ -183,7 +184,7 @@ namespace ChanSort.Loader.Philips
|
||||
if (File.Exists(xmlPath))
|
||||
{
|
||||
try { File.SetAttributes(xmlPath, FileAttributes.Archive);}
|
||||
catch { /**/ }
|
||||
catch(Exception ex) { this.logMessages.AppendLine("Failed to reset file attributes for " + xmlPath + ": " + ex.Message); }
|
||||
this.FileName = xmlPath;
|
||||
var baseName = Path.GetFileNameWithoutExtension(xmlPath).ToUpperInvariant();
|
||||
if (baseName.StartsWith("CM_"))
|
||||
|
||||
@@ -62,14 +62,14 @@ namespace ChanSort.Loader.Samsung.Scm
|
||||
this.OldProgramNr = (short)data.GetWord(_ProgramNr);
|
||||
this.Name = data.GetString(_Name, data.Settings.GetInt("lenName"));
|
||||
this.Favorites = this.ParseRawFavorites();
|
||||
this.Lock = data.GetFlag(_Lock);
|
||||
this.Lock = data.GetFlag(_Lock, false);
|
||||
int hiddenPrimary = data.GetByte(_Hidden);
|
||||
if (hiddenPrimary == 255)
|
||||
this.Hidden = data.GetByte(_HiddenAlt) != 0;
|
||||
else
|
||||
this.Hidden = hiddenPrimary != 0;
|
||||
this.Skip = data.GetFlag(_Skip);
|
||||
this.Encrypted = data.GetFlag(_Encrypted);
|
||||
this.Skip = data.GetFlag(_Skip, false);
|
||||
this.Encrypted = data.GetFlag(_Encrypted, false);
|
||||
this.IsDeleted = data.GetFlag(_Deleted, false) || !data.GetFlag(_IsActive, true);
|
||||
this.AddDebug(data.Data, data.BaseOffset + 25, 3);
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace ChanSort.Loader.Samsung.Scm
|
||||
int count = data.Length / c.avbtFineTuneLength;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bool isCable = mapping.GetFlag("offIsCable", "maskIsCable"); // HACK: this is just a guess
|
||||
bool isCable = mapping.GetFlag("IsCable",false); // HACK: this is just a guess
|
||||
int slot = mapping.GetWord("offSlotNr");
|
||||
float freq = mapping.GetFloat("offFrequency");
|
||||
var dict = isCable ? avbcFrequency : avbtFrequency;
|
||||
|
||||
@@ -12,13 +12,12 @@ namespace ChanSort.Ui
|
||||
private static readonly string ConfigFilePath;
|
||||
|
||||
#region class ColumnInfo
|
||||
|
||||
public class ColumnInfo
|
||||
{
|
||||
[XmlAttribute("name")] public string Name { get; set; }
|
||||
[XmlAttribute("width")] public int Width { get; set; }
|
||||
|
||||
[XmlAttribute("visible")]//[Obsolete("For XML serialization only. Use Visible instead")]
|
||||
[XmlAttribute("visible")]//[Obsolete("For XML serialization only. Use Visible instead")] // won't be serialized/deserialized when marked [Obsolete]
|
||||
public bool VisibleXml
|
||||
{
|
||||
get => this.Visible ?? true;
|
||||
@@ -27,9 +26,7 @@ namespace ChanSort.Ui
|
||||
|
||||
[XmlIgnore]
|
||||
public bool? Visible { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -76,9 +73,6 @@ namespace ChanSort.Ui
|
||||
public bool CheckForUpdates { get; set; } = true;
|
||||
public int FontSizeDelta { get; set; } = 1;
|
||||
|
||||
// deprecated LeftGridLayout and RightGridLayout because they don't preserve order of invisible columns
|
||||
//public string LeftGridLayout { get; set; }
|
||||
//public string RightGridLayout { get; set; }
|
||||
|
||||
[XmlArrayItem("Column")]
|
||||
public List<ColumnInfo> LeftColumns { get; set; } = new();
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using ChanSort.Api;
|
||||
using DevExpress.Data;
|
||||
using DevExpress.XtraGrid.Columns;
|
||||
using DevExpress.XtraGrid.Views.Grid;
|
||||
|
||||
namespace ChanSort
|
||||
{
|
||||
@@ -19,14 +9,16 @@ namespace ChanSort
|
||||
private List<GridColumn> columnOrder = new();
|
||||
private int ignoreEvents;
|
||||
|
||||
#region StoreDefaultColumnOrder()
|
||||
private void StoreDefaultColumnOrder()
|
||||
{
|
||||
// for this to work, the columns in absolute index order must represent the intended visible order
|
||||
this.columnOrder.Clear();
|
||||
this.columnOrder.AddRange(this.Columns.OrderBy(c => c.AbsoluteIndex));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SetColumnOrder
|
||||
#region SetColumnOrder()
|
||||
public void SetColumnOrder(IEnumerable<string> names, bool updateVisibleIndex = true)
|
||||
{
|
||||
// must handle situations where new columns were added to the program, which are not included in the config
|
||||
@@ -85,14 +77,19 @@ namespace ChanSort
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetColumnOrder()
|
||||
public List<GridColumn> GetColumnOrder() => this.columnOrder.ToList();
|
||||
#endregion
|
||||
|
||||
#region RaiseColumnPositionChanged()
|
||||
protected override void RaiseColumnPositionChanged(GridColumn column)
|
||||
{
|
||||
this.OnColumnPositionChanged(column);
|
||||
base.RaiseColumnPositionChanged(column);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OnColumnPositionChanged()
|
||||
private void OnColumnPositionChanged(GridColumn col)
|
||||
{
|
||||
// internal reordering should be ignored
|
||||
@@ -117,7 +114,9 @@ namespace ChanSort
|
||||
}
|
||||
list.Insert(i, col);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SetColumnVisiblity()
|
||||
public void SetColumnVisibility(GridColumn column, bool visible)
|
||||
{
|
||||
if (column.Visible == visible)
|
||||
@@ -144,29 +143,6 @@ namespace ChanSort
|
||||
// fallback
|
||||
column.VisibleIndex = this.VisibleColumns.Count;
|
||||
}
|
||||
|
||||
//protected override void SetColumnVisibleIndex(GridColumn column, int newValue)
|
||||
//{
|
||||
// int oldVisIndex = column.VisibleIndex;
|
||||
// int newIdx = newValue > column.VisibleIndex ? newValue - 1 : newValue;
|
||||
// base.SetColumnVisibleIndex(column, newValue);
|
||||
|
||||
// if (newIdx < 0 || oldVisIndex == newIdx)
|
||||
// {
|
||||
// // hide or no change: keep as-is
|
||||
// }
|
||||
// else if (newIdx >= 0)
|
||||
// {
|
||||
// // move
|
||||
// columnOrder.Remove(column);
|
||||
// if (newIdx == 0)
|
||||
// columnOrder.Insert(0, column);
|
||||
// else
|
||||
// {
|
||||
// var afterCol = this.VisibleColumns[column.VisibleIndex - 1];
|
||||
// columnOrder.Insert(columnOrder.IndexOf(afterCol) + 1, column);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,36 +15,36 @@ struct SHeader
|
||||
|
||||
struct SChannel_fta
|
||||
{
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
char chName1[200];
|
||||
uint16 u3;
|
||||
uint8 u3b[208];
|
||||
uint8 u3c[2];
|
||||
uint16 u3d;
|
||||
uint8 u4[10];
|
||||
uint32 freqInMhz1;
|
||||
//uint16 u5;
|
||||
uint16 u6;
|
||||
uint16 symRate;
|
||||
uint32 oldProgNr;
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 u7;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
uint16 freqInMhz2;
|
||||
uint8 padding[6];
|
||||
uint16 u3;
|
||||
uint8 u3b[208];
|
||||
uint8 u3c[2];
|
||||
uint16 u3d;
|
||||
uint8 u4[10];
|
||||
uint32 freqInMhz1;
|
||||
//uint16 u5;
|
||||
uint16 u6;
|
||||
uint16 symRate;
|
||||
uint32 oldProgNr;
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 u7;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
uint16 freqInMhz2;
|
||||
uint8 padding[6];
|
||||
};
|
||||
|
||||
struct SFooter
|
||||
{
|
||||
uint32 numDataBlocks;
|
||||
uint32 numDataBlockBytes;
|
||||
uint16 bytesumFrom0;
|
||||
uint16 u_zero;
|
||||
uint32 numDataBlocks;
|
||||
uint32 numDataBlockBytes;
|
||||
uint16 bytesumFrom0;
|
||||
uint16 u_zero;
|
||||
};
|
||||
|
||||
#pragma script("get_doc_size.js")
|
||||
@@ -54,95 +54,127 @@ public struct Philips_mgr_chan_s_fta
|
||||
var docSize = GetDocumentSize();
|
||||
|
||||
char filename[32];
|
||||
|
||||
SHeader header;
|
||||
|
||||
SHeader header;
|
||||
|
||||
var recordCount = header.channelBlockSize / sizeof(SChannel_fta);
|
||||
var recordCount = header.channelBlockSize / sizeof(SChannel_fta);
|
||||
SChannel_fta channels[recordCount];
|
||||
|
||||
SFooter footer;
|
||||
|
||||
SFooter footer;
|
||||
};
|
||||
|
||||
//#########################################################
|
||||
|
||||
struct SChannel_pkg
|
||||
struct SChannel_pkg_476
|
||||
{
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
char chName1[200];
|
||||
uint16 u3;
|
||||
uint8 u3b[208];
|
||||
uint8 u3c[2];
|
||||
uint16 u3d;
|
||||
uint8 u4[10];
|
||||
uint32 freqInMhz1;
|
||||
//uint16 u5;
|
||||
uint16 u6;
|
||||
uint16 symRate;
|
||||
uint32 oldProgNr;
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 u7;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
uint16 freqInMhz2;
|
||||
uint8 padding[10];
|
||||
uint16 u3;
|
||||
uint8 u3b[208];
|
||||
uint8 u3c[2];
|
||||
uint16 u3d;
|
||||
uint8 u4[10];
|
||||
uint32 freqInMhz1;
|
||||
uint16 u6;
|
||||
uint16 symRate;
|
||||
uint32 oldProgNr;
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 u7;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
uint16 freqInMhz2;
|
||||
uint8 padding[6];
|
||||
// some files have this additional size of 4 bytes
|
||||
//uint8 padding2[4];
|
||||
};
|
||||
|
||||
public struct Philips_mgr_chan_s_pkg
|
||||
public struct Philips_mgr_chan_s_pkg_476
|
||||
{
|
||||
var docSize = GetDocumentSize();
|
||||
|
||||
char filename[32];
|
||||
|
||||
SHeader header;
|
||||
SHeader header;
|
||||
var recordCount = header.channelBlockSize / sizeof(SChannel_pkg_476);
|
||||
SChannel_pkg_476 channels[recordCount];
|
||||
SFooter footer;
|
||||
};
|
||||
|
||||
var recordCount = header.channelBlockSize / sizeof(SChannel_pkg);
|
||||
SChannel_pkg channels[recordCount];
|
||||
|
||||
SFooter footer;
|
||||
struct SChannel_pkg_480
|
||||
{
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
char chName1[200];
|
||||
uint16 u3;
|
||||
uint8 u3b[208];
|
||||
uint8 u3c[2];
|
||||
uint16 u3d;
|
||||
uint8 u4[10];
|
||||
uint32 freqInMhz1;
|
||||
uint16 u6;
|
||||
uint16 symRate;
|
||||
uint32 oldProgNr;
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 u7;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
uint16 freqInMhz2;
|
||||
uint8 padding[6];
|
||||
// some files have this additional size of 4 bytes
|
||||
uint8 padding2[4];
|
||||
};
|
||||
|
||||
public struct Philips_mgr_chan_s_pkg_480
|
||||
{
|
||||
char filename[32];
|
||||
SHeader header;
|
||||
var recordCount = header.channelBlockSize / sizeof(SChannel_pkg_480);
|
||||
SChannel_pkg_480 channels[recordCount];
|
||||
SFooter footer;
|
||||
};
|
||||
|
||||
//#########################################################
|
||||
|
||||
struct CChannel
|
||||
{
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
union
|
||||
{
|
||||
char chName1[200];
|
||||
uint32 curProgNr;
|
||||
uint32 u1;
|
||||
uint8 u2[8];
|
||||
uint32 favNr;
|
||||
union
|
||||
{
|
||||
char chName1[200];
|
||||
#pragma byte_order (BigEndian)
|
||||
big_endian wchar_t chName2[100];
|
||||
big_endian wchar_t chName2[100];
|
||||
#pragma byte_order ()
|
||||
struct
|
||||
{
|
||||
uint8 zero;
|
||||
wchar_t chName3[99];
|
||||
uint8 zero2;
|
||||
} chName4;
|
||||
} chName;
|
||||
uint16 u3;
|
||||
uint16 u3b;
|
||||
char provider[200];
|
||||
uint8 u4[16];
|
||||
uint32 freqInHz;
|
||||
uint16 u6;
|
||||
uint16 not_symRate;
|
||||
uint32 oldProgNr;
|
||||
uint8 u7[4];
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 symRate_maybe;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
//uint16 freqInMhz2;
|
||||
//uint16 u9;
|
||||
uint32 u10;
|
||||
struct
|
||||
{
|
||||
uint8 zero;
|
||||
wchar_t chName3[99];
|
||||
uint8 zero2;
|
||||
} chName4;
|
||||
} chName;
|
||||
uint16 u3;
|
||||
uint16 u3b;
|
||||
char provider[200];
|
||||
uint8 u4[16];
|
||||
uint32 freqInHz;
|
||||
uint16 u6;
|
||||
uint16 not_symRate;
|
||||
uint32 oldProgNr;
|
||||
uint8 u7[4];
|
||||
uint32 channelIndex;
|
||||
uint16 tsid;
|
||||
uint16 symRate_maybe;
|
||||
uint16 sid;
|
||||
uint16 onid;
|
||||
//uint16 freqInMhz2;
|
||||
//uint16 u9;
|
||||
uint32 u10;
|
||||
};
|
||||
|
||||
|
||||
@@ -151,75 +183,225 @@ public struct Philips_mgr_chan_dvbt
|
||||
var docSize = GetDocumentSize();
|
||||
|
||||
char filename[32];
|
||||
|
||||
SHeader header;
|
||||
|
||||
SHeader header;
|
||||
|
||||
var recordCount = header.channelBlockSize / sizeof(CChannel);
|
||||
var recordCount = header.channelBlockSize / sizeof(CChannel);
|
||||
CChannel channels[recordCount];
|
||||
|
||||
SFooter footer;
|
||||
|
||||
SFooter footer;
|
||||
};
|
||||
|
||||
#pragma byte_order(LittleEndian)
|
||||
|
||||
//*****************************************************************************************
|
||||
// FLASH files
|
||||
//*****************************************************************************************
|
||||
|
||||
struct ProgNr
|
||||
{
|
||||
WORD nr : 14;
|
||||
WORD flags : 2;
|
||||
WORD nr : 14;
|
||||
WORD flags : 2;
|
||||
};
|
||||
|
||||
struct s_channelTransponder
|
||||
{
|
||||
struct
|
||||
{
|
||||
WORD u1 : 3;
|
||||
WORD isRadio: 1;
|
||||
WORD u2 : 1;
|
||||
WORD transponderId : 11;
|
||||
} info;
|
||||
WORD channelId;
|
||||
};
|
||||
|
||||
struct s_transponder
|
||||
{
|
||||
WORD id;
|
||||
BYTE source_maybe;
|
||||
BYTE satId_maybe;
|
||||
WORD freqInMhz;
|
||||
WORD symbolRate;
|
||||
BYTE u2[2];
|
||||
WORD onid;
|
||||
WORD tsid;
|
||||
WORD nid;
|
||||
BYTE u3[5];
|
||||
WORD freqInMhz2;
|
||||
BYTE u4[8];
|
||||
// optional:
|
||||
BYTE u5[5];
|
||||
};
|
||||
|
||||
struct s_satTransponder
|
||||
{
|
||||
WORD satId_maybe;
|
||||
WORD transponderId;
|
||||
};
|
||||
|
||||
struct s_unknown
|
||||
{
|
||||
WORD u1;
|
||||
BYTE u2[2];
|
||||
DWORD zero;
|
||||
};
|
||||
|
||||
struct s_channel
|
||||
{
|
||||
WORD id;
|
||||
BYTE u1[4];
|
||||
BYTE u2[2];
|
||||
WORD sid;
|
||||
WORD pcrPid;
|
||||
WORD vpid;
|
||||
BYTE u3[8];
|
||||
ProgNr progNr;
|
||||
BYTE u3b[7];
|
||||
WORD apid1_maybe;
|
||||
char lang1[3];
|
||||
BYTE u4[45];
|
||||
WORD apid2_maybe;
|
||||
char lang2[3];
|
||||
BYTE u5[3];
|
||||
// optional
|
||||
BYTE u6[4];
|
||||
};
|
||||
|
||||
struct s_channelBlock
|
||||
{
|
||||
char ddtc[4];
|
||||
s_channel channels[734];
|
||||
BYTE filler[0x20000-0x1ff32];
|
||||
};
|
||||
|
||||
#define numChannelTransponderMap 5000
|
||||
#define numSatTransponderMap 1090
|
||||
#define numTransponder 1100
|
||||
#define numSatData 4592
|
||||
#define numUnknown 310*8
|
||||
public struct Philips_FLASH_DTVINFO_S_FTA
|
||||
{
|
||||
char ddtc[4];
|
||||
var off0 = current_offset;
|
||||
struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
WORD u1 : 3;
|
||||
WORD isRadio: 1;
|
||||
WORD u2 : 1;
|
||||
WORD transponderIndex : 11;
|
||||
} info;
|
||||
WORD chanIdx;
|
||||
} chanIndexInfo[(0x5f2c - off0)/4];
|
||||
|
||||
off0 = current_offset;
|
||||
struct s_transponder
|
||||
{
|
||||
var off1 = current_offset;
|
||||
WORD id;
|
||||
WORD u1;
|
||||
WORD freqInMhz;
|
||||
WORD symbolRate;
|
||||
BYTE u2[2];
|
||||
WORD onid;
|
||||
WORD tsid;
|
||||
WORD nid;
|
||||
BYTE u3[5];
|
||||
WORD freqInMhz2;
|
||||
BYTE u4[8];
|
||||
} transponder[(0x10000 - off0)/sizeof(s_transponder)];
|
||||
|
||||
BYTE filler[4];
|
||||
char ddtc2[4];
|
||||
|
||||
struct s_channel
|
||||
{
|
||||
WORD idx1;
|
||||
BYTE u1[4];
|
||||
BYTE u2[2];
|
||||
WORD sid;
|
||||
WORD pcrPid;
|
||||
WORD vpid;
|
||||
BYTE u3[8];
|
||||
ProgNr progNr;
|
||||
BYTE u3b[7];
|
||||
WORD apid1_maybe;
|
||||
char lang1[3];
|
||||
BYTE u4[45];
|
||||
WORD apid2_maybe;
|
||||
char lang2[3];
|
||||
BYTE u5[3];
|
||||
} channel[*];
|
||||
};
|
||||
char ddtc[4];
|
||||
s_channelTransponder channelTranponderMap[numChannelTransponderMap];
|
||||
s_satTransponder satTransponderMap[numSatTransponderMap];
|
||||
s_transponder transponder[numTransponder];
|
||||
BYTE satData[numSatData];
|
||||
s_unknown unkData[numUnknown];
|
||||
s_channelBlock channelBlocks[*];
|
||||
};
|
||||
|
||||
#undef numChannelTransponderMap
|
||||
#define numChannelTransponderMap 2000
|
||||
#undef numSatTransponderMap
|
||||
#define numSatTransponderMap 590
|
||||
#undef numTransponder
|
||||
#define numTransponder 550
|
||||
#undef numSatData
|
||||
#define numSatData 4592
|
||||
#undef numUnknown
|
||||
#define numUnknown 310
|
||||
public struct Philips_FLASH_DTVINFO_S_PKG
|
||||
{
|
||||
char ddtc[4];
|
||||
s_channelTransponder channelTranponderMap[numChannelTransponderMap];
|
||||
s_satTransponder satTransponderMap[numSatTransponderMap];
|
||||
s_transponder transponder[numTransponder];
|
||||
BYTE satData[numSatData];
|
||||
var off0 = current_offset;
|
||||
BYTE unk[0x10000 - off0];
|
||||
s_channelBlock channelBlocks[*];
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
struct s_channelTransponder2
|
||||
{
|
||||
struct
|
||||
{
|
||||
WORD u1 : 3;
|
||||
WORD isRadio: 1;
|
||||
WORD u2 : 1;
|
||||
WORD transponderId : 11;
|
||||
} info;
|
||||
WORD channelId;
|
||||
};
|
||||
|
||||
struct s_transponder2
|
||||
{
|
||||
WORD id;
|
||||
BYTE source_maybe;
|
||||
BYTE satId_maybe;
|
||||
WORD freqInMhz;
|
||||
WORD symbolRate;
|
||||
BYTE u2[2];
|
||||
WORD onid;
|
||||
WORD tsid;
|
||||
WORD nid;
|
||||
BYTE u3[5];
|
||||
WORD freqInMhz2;
|
||||
BYTE u4[8];
|
||||
BYTE u5[5];
|
||||
};
|
||||
|
||||
struct s_satTransponder2
|
||||
{
|
||||
WORD satId_maybe;
|
||||
WORD transponderId;
|
||||
};
|
||||
|
||||
struct s_unknown2
|
||||
{
|
||||
WORD u1;
|
||||
BYTE u2[2];
|
||||
DWORD zero;
|
||||
};
|
||||
|
||||
struct s_channel2
|
||||
{
|
||||
WORD id;
|
||||
BYTE u1[4];
|
||||
BYTE u2[2];
|
||||
WORD sid;
|
||||
WORD pcrPid;
|
||||
WORD vpid;
|
||||
BYTE u3[10];
|
||||
ProgNr progNr;
|
||||
BYTE u3b[8];
|
||||
WORD apid1_maybe;
|
||||
char lang1[3];
|
||||
BYTE u4[45];
|
||||
WORD apid2_maybe;
|
||||
char lang2[3];
|
||||
BYTE u5[3];
|
||||
BYTE u6[1];
|
||||
};
|
||||
|
||||
struct s_channelBlock2
|
||||
{
|
||||
char ddtc[4];
|
||||
s_channel2 channels[703];
|
||||
BYTE filler[0x20000 - 0x1ff67];
|
||||
};
|
||||
|
||||
#undef numChannelTransponderMap
|
||||
#define numChannelTransponderMap 5000
|
||||
#undef numSatTransponderMap
|
||||
#define numSatTransponderMap 60
|
||||
#undef numTransponder
|
||||
#define numTransponder 1100
|
||||
#undef numSatData
|
||||
#define numSatData 3512
|
||||
#undef numUnknown
|
||||
#define numUnknown 2180
|
||||
public struct Philips_FLASH_DTVINFO_S_FTA_for_pkg
|
||||
{
|
||||
char ddtc[4];
|
||||
s_channelTransponder2 channelTranponderMap[numChannelTransponderMap];
|
||||
s_satTransponder2 satTransponderMap[numSatTransponderMap];
|
||||
s_transponder2 transponder[numTransponder];
|
||||
BYTE satData[numSatData];
|
||||
BYTE unknown[numUnknown];
|
||||
s_channelBlock2 channelBlocks[*];
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user