diff --git a/source/ChanSort.Api/Model/ChannelInfo.cs b/source/ChanSort.Api/Model/ChannelInfo.cs
index eed03f3..390696b 100644
--- a/source/ChanSort.Api/Model/ChannelInfo.cs
+++ b/source/ChanSort.Api/Model/ChannelInfo.cs
@@ -96,7 +96,12 @@ namespace ChanSort.Api
/// A proxy channel is inserted into the current channel list when there was no match for a reference list channel
///
public bool IsProxy => this.RecordIndex == -1;
-
+
+ ///
+ /// can be used to store the offset of the raw data in the input file, e.g. for re-parsing names with different encoding
+ ///
+ public int RawDataOffset { get; set; }
+
///
/// arbitrary information that can be shown in a UI column to assist in analyzing a file format while coding a plugin
///
diff --git a/source/ChanSort.Api/Model/Transponder.cs b/source/ChanSort.Api/Model/Transponder.cs
index 94b3c3b..e7caf41 100644
--- a/source/ChanSort.Api/Model/Transponder.cs
+++ b/source/ChanSort.Api/Model/Transponder.cs
@@ -14,6 +14,8 @@
public int TransportStreamId { get; set; }
public SignalSource SignalSource { get; set; }
+ public string Name { get; set; }
+
public Transponder(int id)
{
this.id = id;
diff --git a/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.csproj b/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.csproj
index c6ff69b..c5cc766 100644
--- a/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.csproj
+++ b/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.csproj
@@ -50,4 +50,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.ini b/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.ini
index 5b3d3c5..9180623 100644
--- a/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.ini
+++ b/source/ChanSort.Loader.Hisense/ChanSort.Loader.Hisense.ini
@@ -9,10 +9,14 @@ RecordSize=328
ID=0
BroadcastType=2
ServiceType=3
+Nid=4
+Onid=6
+Tsid=8
Frequency=16
-SymbolRate=28
+SymbolRate=20
+DvbsSymbolRate=28
Name=216
-NameLength=96
+NameLength=32
[SVL_Record]
@@ -21,33 +25,32 @@ RecordSize=304
RecordId=0
ChannelId=6
-HashcodeFieldMask=8
-HashcodeFieldMask_Name=0x01
-HashcodeFieldMask_ChannelId=0x02
-HashcodeFieldMask_BroadcastType=0x04
-HashcodeFieldMask_TslRecId=0x08
-HashcodeFieldMask_PrgNum=0x10
-HashcodeFieldMask_ShortName=0x20
-HashcodeFieldMask_Radio = 0x0400
-HashcodeFieldMask_Encrypted = 0x0800
-HashcodeFieldMask_Tv = 0x2000
+Hashcode=8
+Hashcode_Name=0x01
+Hashcode_ServiceId=0x02
+Hashcode_BroadcastType=0x04
+;Hashcode_TslRecId=0x08
+;Hashcode_PrgNum=0x10
+;Hashcode_ShortName=0x20
-NwMask=12
+NwMask=8
NwMask_Skip = 0x08
NwMask_Fav1 = 0x10
NwMask_Fav2 = 0x20
NwMask_Fav3 = 0x40
NwMask_Fav4 = 0x80
-NwMask_Lock = 0x80
-NwMask_Hide = 0x00
+NwMask_Lock = 0x100
+NwMask_Radio = 0x0400
+NwMask_Encrypted = 0x0800
+NwMask_Tv = 0x2000
-OptionMask=16
+OptionMask=12
MaskRename = 0x08
MaskMoved = 0x400
-OptionMask2=20
+OptionMask2=16
-ProgramId=24
+ServiceId=24
TslTableId=26
TslRecordId=28
NwlTableId=30
@@ -63,16 +66,22 @@ BroadcastSystemData=136
[DVB_Data]
ShortName=4
-ShortName_Size=16
+ShortNameLength=16
LinkageMask=28
LinkageMask_Ts=0x04
Onid=44
Tsid=46
-Ssid=48
-Encrypted=64
+Sid=48
+DvbcTsid=118
+DvbcOnid=120
+ServiceType=129
-[Columns]
-DVB_T=Position,OldProgramNr,Name,ShortName,Favorites,Lock,Skip,Encrypted,ChannelOrTransponder,FreqInMhz,ServiceTypeName
-DVB_C=Position,OldProgramNr,Name,ShortName,Favorites,Lock,Skip,Encrypted,ChannelOrTransponder,FreqInMhz,ServiceTypeName,SymbolRate
-DVB_S=Position,OldProgramNr,Name,ShortName,Favorites,Lock,Skip,Encrypted,FreqInMhz,ServiceTypeName,SymbolRate
+[FAV_Record]
+RecordSize=80
+SvlTableId=0
+SvlRecordId=2
+DisplayNumber=4
+DisplayNumberLength=10
+ChannelName=15
+ChannelNameLength=64
diff --git a/source/ChanSort.Loader.Hisense/HisBin/HisBinSerializer.cs b/source/ChanSort.Loader.Hisense/HisBin/HisBinSerializer.cs
new file mode 100644
index 0000000..c23382d
--- /dev/null
+++ b/source/ChanSort.Loader.Hisense/HisBin/HisBinSerializer.cs
@@ -0,0 +1,432 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using ChanSort.Api;
+
+namespace ChanSort.Loader.Hisense.HisBin;
+
+/*
+ * Loads Hisense HIS_SVL.BIN channel lists
+ *
+ * This binary format is based on a customized MediaTek format, which means that there may be many incompatible
+ * variants that can't be identified and distinguished easily.
+ * This loader only supports the known Hisense variant with 304 bytes per channel in HIS_SVL.BIN.
+ *
+ * See also the his-svl.h file in Information/FileStructures_for_HHD_Hex_Editor_Neo
+ *
+ * Some properties of these lists:
+ * - channel records are physically ordered by their program number. All TV channels first, then radio, then data.
+ * - favorite lists allow mixing channels from different inputs and also radio and TV
+ * - character encoding seen as both implicit UTF8 or latin-1
+ */
+public class HisBinSerializer : SerializerBase
+{
+ private readonly ChannelList dvbtChannels = new (SignalSource.DvbT | SignalSource.Tv | SignalSource.Radio, "DVB-T");
+ private readonly ChannelList dvbcChannels = new (SignalSource.DvbC | SignalSource.Tv | SignalSource.Radio, "DVB-C");
+ private readonly ChannelList dvbsChannels = new (SignalSource.DvbS | SignalSource.Tv | SignalSource.Radio, "DVB-S");
+ private readonly ChannelList favChannels = new(SignalSource.All, "Fav") { IsMixedSourceFavoritesList = true };
+
+ private byte[] svlFileContent;
+ private byte[] tslFileContent;
+ private const int MaxFileSize = 4 << 20; // 4 MB
+
+ private int tSize, cSize, sSize;
+
+ private const string ERR_fileTooBig = "The file size {0} is larger than the allowed maximum of {1}.";
+ private const string ERR_badFileFormat = "The content of the file doesn't match the expected format.";
+
+ private DataMapping svlMapping, tslMapping, dvbMapping, favMapping;
+ private readonly Dictionary transponder = new ();
+
+ #region ctor()
+ public HisBinSerializer(string inputFile) : base(inputFile)
+ {
+ this.Features.ChannelNameEdit = ChannelNameEditMode.All;
+ this.Features.CanSkipChannels = true;
+ this.Features.CanLockChannels = true;
+ this.Features.CanHideChannels = false;
+ this.Features.FavoritesMode = FavoritesMode.MixedSource;
+ this.Features.MaxFavoriteLists = 4;
+ this.ReadConfigurationFromIniFile();
+
+ this.DataRoot.AddChannelList(dvbcChannels);
+ this.DataRoot.AddChannelList(dvbtChannels);
+ this.DataRoot.AddChannelList(dvbsChannels);
+ this.DataRoot.AddChannelList(favChannels);
+ foreach (var list in this.DataRoot.ChannelLists)
+ {
+ list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.PcrPid));
+ list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.VideoPid));
+ list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.AudioPid));
+ list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Satellite));
+ list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceType));
+ }
+ }
+ #endregion
+
+ #region ReadConfigurationFromIniFile()
+
+ private void ReadConfigurationFromIniFile()
+ {
+ string iniFile = this.GetType().Assembly.Location.ToLower().Replace(".dll", ".ini");
+ IniFile ini = new IniFile(iniFile);
+ this.svlMapping = new DataMapping(ini.GetSection("SVL_Record"));
+ this.svlMapping.DefaultEncoding = this.DefaultEncoding;
+ this.tslMapping = new DataMapping(ini.GetSection("TSL_Record"));
+ this.tslMapping.DefaultEncoding = this.DefaultEncoding;
+ this.dvbMapping = new DataMapping(ini.GetSection("DVB_Data"));
+ this.dvbMapping.DefaultEncoding = this.DefaultEncoding;
+ this.favMapping = new DataMapping(ini.GetSection("FAV_Record"));
+ }
+ #endregion
+
+
+ #region Load()
+
+ public override void Load()
+ {
+ var dir = Path.GetDirectoryName(this.FileName);
+ var name = Path.GetFileNameWithoutExtension(this.FileName);
+ var i = name.LastIndexOf('_');
+ var basename = i < 0 ? name : name.Substring(0, i);
+ this.FileName = Path.Combine(dir, basename + "_SVL.BIN");
+ this.LoadTslFile(Path.Combine(dir, basename + "_TSL.BIN"));
+ this.LoadSvlFile(this.FileName);
+ this.LoadFavFile(Path.Combine(dir, basename + "_FAV.BIN"));
+ }
+
+ #endregion
+
+ #region LoadTslFile()
+ private void LoadTslFile(string fileName)
+ {
+ long fileSize = new FileInfo(fileName).Length;
+ if (fileSize > MaxFileSize)
+ throw new FileLoadException(string.Format(ERR_fileTooBig, fileSize, MaxFileSize));
+ this.tslFileContent = File.ReadAllBytes(fileName);
+ int off = 0;
+
+ tSize = this.ReadHeader(tslFileContent, ref off);
+ cSize = this.ReadHeader(tslFileContent, ref off);
+ sSize = this.ReadHeader(tslFileContent, ref off);
+ this.ReadTransponder(ref off, tSize, 1, 1000000);
+ this.ReadTransponder(ref off, cSize, 2, 1000000);
+ this.ReadTransponder(ref off, sSize, 3, 1);
+ }
+ #endregion
+
+ #region ReadTransponder()
+ private void ReadTransponder(ref int off, int size, int table, int freqFactor)
+ {
+ int recordSize = tslMapping.Settings.GetInt("RecordSize");
+ if (size % recordSize != 0)
+ throw new FileLoadException(ERR_badFileFormat);
+ int count = size / recordSize;
+ if (count == 0)
+ return;
+
+ for (int i = 0; i < count; i++)
+ {
+ tslMapping.SetDataPtr(tslFileContent, off);
+ var id = (table << 16) + tslMapping.GetWord("ID");
+ var trans = new Transponder(id);
+ trans.FrequencyInMhz = (decimal)tslMapping.GetDword("Frequency") / freqFactor;
+ var sym = tslMapping.GetDword("SymbolRate");
+ if (sym == 0)
+ sym = tslMapping.GetDword("DvbsSymbolRate");
+ trans.SymbolRate = (int)(sym > 1000000 ? sym / 1000 : sym);
+ trans.OriginalNetworkId = tslMapping.GetWord("Onid");
+ if (trans.OriginalNetworkId == 0) // some files have Onid=0 but provide a Nid, which seems to be the Onid
+ trans.OriginalNetworkId = tslMapping.GetWord("Nid");
+ trans.TransportStreamId = tslMapping.GetWord("Tsid");
+ trans.Name = tslMapping.GetString("Name", tslMapping.Settings.GetInt("NameLength"));
+ var z = trans.Name.IndexOf('\0');
+ if (z >= 0)
+ trans.Name = trans.Name.Substring(0, z);
+ this.transponder.Add(id, trans);
+ off += recordSize;
+ }
+ }
+ #endregion
+
+ #region LoadSvlFile()
+
+ private void LoadSvlFile(string fileName)
+ {
+ long fileSize = new FileInfo(fileName).Length;
+ if (fileSize > MaxFileSize)
+ throw new FileLoadException(string.Format(ERR_fileTooBig, fileSize, MaxFileSize));
+ this.svlFileContent = File.ReadAllBytes(this.FileName);
+ int off = 0;
+
+ tSize = this.ReadHeader(svlFileContent, ref off);
+ cSize = this.ReadHeader(svlFileContent, ref off);
+ sSize = this.ReadHeader(svlFileContent, ref off);
+ this.ReadChannelList(ref off, tSize, 1, dvbtChannels);
+ this.ReadChannelList(ref off, cSize, 2, dvbcChannels);
+ this.ReadChannelList(ref off, sSize, 3, dvbsChannels);
+ }
+ #endregion
+
+ #region ReadHeader()
+ private int ReadHeader(byte[] data, ref int off)
+ {
+ if (off + 40 > data.Length)
+ throw new FileLoadException(ERR_badFileFormat);
+ var blockSize = BitConverter.ToInt32(data, off + 36);
+ if (off + blockSize > data.Length)
+ throw new FileLoadException(ERR_badFileFormat);
+
+ off += 40;
+ return blockSize;
+ }
+ #endregion
+
+ #region ReadChannelList()
+ private void ReadChannelList(ref int off, int size, int table, ChannelList channels)
+ {
+ int recordSize = svlMapping.Settings.GetInt("RecordSize");
+ if (size % recordSize != 0)
+ throw new FileLoadException(ERR_badFileFormat);
+ int channelCount = size / recordSize;
+ if (channelCount == 0)
+ return;
+
+ var broadcastDataOffset = svlMapping.Settings.GetInt("BroadcastSystemData");
+ var nameLength = svlMapping.Settings.GetInt("NameLength");
+ var source = channels.SignalSource & (SignalSource.MaskAnalogDigital | SignalSource.MaskAntennaCableSat);
+ for (int i = 0; i < channelCount; i++)
+ {
+ svlMapping.SetDataPtr(svlFileContent, off);
+ dvbMapping.SetDataPtr(svlFileContent, off + broadcastDataOffset);
+ var ci = ReadChannel(source, i, nameLength);
+ if (ci != null)
+ {
+ this.DataRoot.AddChannel(channels, ci);
+ this.favChannels.AddChannel(ci);
+ }
+
+ off += recordSize;
+ }
+ }
+ #endregion
+
+ #region ReadChannel()
+ private ChannelInfo ReadChannel(SignalSource source, int index, int nameLength)
+ {
+ var id = svlMapping.GetWord("RecordId");
+ ChannelInfo ci = new ChannelInfo(source, id, 0, "");
+ ci.RecordOrder = index;
+ ci.OldProgramNr = svlMapping.GetWord("ChannelId") >> 2;
+ ci.RawDataOffset = svlMapping.BaseOffset;
+
+ var nwMask = svlMapping.GetDword("NwMask");
+ ci.Skip = (nwMask & svlMapping.Settings.GetInt("NwMask_Skip")) == 0; // reverse logic!
+ ci.Lock = (nwMask & svlMapping.Settings.GetInt("NwMask_Lock")) != 0;
+ for (int i = 1; i <= 4; i++)
+ {
+ bool isFav = (nwMask & svlMapping.Settings.GetInt("NwMask_Fav" + i)) != 0;
+ if (isFav)
+ ci.Favorites |= (Favorites)(1 << (i-1));
+ }
+ ci.Encrypted = (nwMask & svlMapping.Settings.GetInt("NwMask_Encrypted")) != 0;
+
+ ci.Name = ReadString(svlMapping, "Name", nameLength);
+
+ var serviceType = svlMapping.GetByte("ServiceType");
+ if (serviceType == 1)
+ {
+ ci.SignalSource |= SignalSource.Tv;
+ ci.ServiceTypeName = "TV";
+ }
+ else if (serviceType == 2)
+ {
+ ci.SignalSource |= SignalSource.Radio;
+ ci.ServiceTypeName = "Radio";
+ }
+ else
+ {
+ ci.ServiceTypeName = "Data";
+ }
+
+ ci.ServiceId = svlMapping.GetWord("ServiceId");
+
+ int transpTableId = svlMapping.GetWord("TslTableId");
+ int transpRecordId = svlMapping.GetWord("TslRecordId");
+ var transpId = (transpTableId << 16) + transpRecordId;
+ var transp = this.transponder.TryGet(transpId);
+ if (transp != null)
+ {
+ ci.Transponder = transp;
+ ci.FreqInMhz = transp.FrequencyInMhz;
+ ci.SymbolRate = transp.SymbolRate;
+ switch (ci.SymbolRate) // can be either an enum for bandwidth 1=6 MHz, 2=7 MHz, 3=8 MHz - or the symbol rate
+ {
+ case 1: ci.SymbolRate = 6000; break;
+ case 2: ci.SymbolRate = 7000; break;
+ case 3: ci.SymbolRate = 8000; break;
+ }
+ ci.OriginalNetworkId = transp.OriginalNetworkId;
+ ci.TransportStreamId = transp.TransportStreamId;
+ ci.Provider = transp.Name;
+ }
+
+ var bcastType = svlMapping.GetByte("BroadcastType");
+ if (bcastType == 1)
+ ReadAnalogData(ci);
+ else if (bcastType == 2)
+ ReadDvbData(ci);
+
+ return ci;
+ }
+ #endregion
+
+ #region ReadAnalogData()
+ private void ReadAnalogData(ChannelInfo ci)
+ {
+
+ }
+ #endregion
+
+ #region ReadDvbData()
+ private void ReadDvbData(ChannelInfo ci)
+ {
+ var mask = dvbMapping.GetDword("LinkageMask");
+ var tsFlag = dvbMapping.Settings.GetInt("LinkageMask_Ts");
+
+ if ((mask & tsFlag) != 0)
+ {
+ ci.OriginalNetworkId = dvbMapping.GetWord("Onid");
+ ci.TransportStreamId = dvbMapping.GetWord("Tsid");
+ ci.ServiceId = dvbMapping.GetWord("Sid");
+ }
+
+ if ((ci.SignalSource & (SignalSource.MaskAnalogDigital | SignalSource.MaskAntennaCableSat)) == SignalSource.DvbC)
+ {
+ ci.OriginalNetworkId = dvbMapping.GetWord("DvbcOnid");
+ ci.TransportStreamId = dvbMapping.GetWord("DvbcTsid");
+ }
+
+ if ((ci.SignalSource & SignalSource.DvbT) == SignalSource.DvbT)
+ ci.ChannelOrTransponder = LookupData.Instance.GetDvbtTransponder(ci.FreqInMhz).ToString();
+ else if ((ci.SignalSource & SignalSource.DvbC) == SignalSource.DvbC)
+ ci.ChannelOrTransponder = LookupData.Instance.GetDvbcChannelName(ci.FreqInMhz).ToString();
+
+ ci.ServiceType = dvbMapping.GetByte("ServiceType");
+ if (ci.ServiceType != 0)
+ ci.ServiceTypeName = LookupData.Instance.GetServiceTypeDescription(ci.ServiceType);
+
+ ci.ShortName = dvbMapping.GetString("ShortName", dvbMapping.Settings.GetInt("ShortNameLength"));
+ }
+ #endregion
+
+ #region ReadString()
+ private string ReadString(DataMapping mapping, string name, int nameLength)
+ {
+ var str = mapping.GetString(name, nameLength);
+ int term = str.IndexOf('\0');
+ if (term >= 0)
+ str = str.Substring(0, term);
+ return str;
+ }
+ #endregion
+
+ #region LoadFavFile()
+ private void LoadFavFile(string filename)
+ {
+ if (!File.Exists(filename))
+ return;
+ var content = File.ReadAllBytes(filename);
+ var favListSizes = new int[4];
+ for (int i = 0; i < 4; i++)
+ favListSizes[i] = BitConverter.ToInt32(content, i * 4);
+
+ var recSize = favMapping.Settings.GetInt("RecordSize");
+ var dispNumLen = favMapping.Settings.GetInt("DisplayNumberLength");
+ favMapping.SetDataPtr(content, 16 - recSize);
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0, c = favListSizes[i] / recSize; j < c; j++)
+ {
+ favMapping.BaseOffset += recSize;
+
+ //if (favMapping.BaseOffset + recSize >= content.Length) //
+ // break;
+
+ var tblId = favMapping.GetWord("SvlTableId");
+ var recId = favMapping.GetWord("SvlRecordId");
+
+ var list = tblId == 1 ? dvbtChannels : tblId == 2 ? dvbcChannels : tblId == 3 ? dvbsChannels : null;
+ if (list == null) // should never happen
+ continue;
+
+ var chan = list.GetChannelById(recId);
+ if (chan == null) // should never happen
+ continue;
+
+ var dispNr = favMapping.GetString("DisplayNumber", dispNumLen);
+ var nr = int.Parse(dispNr);
+ chan.SetOldPosition(1 + i, nr);
+ }
+ }
+ }
+ #endregion
+
+ // Saving ====================================
+
+ #region Save()
+ public override void Save()
+ {
+ throw new NotImplementedException();
+
+ // TODO
+ // HIS_SVL
+ // - update size in header
+ // - write channels in new physical order
+ // HIS_FAV
+ }
+ #endregion
+
+ // Infrastructure ============================
+
+ #region DefaultEncoding
+ public override Encoding DefaultEncoding
+ {
+ get => base.DefaultEncoding;
+ set
+ {
+ if (value == this.DefaultEncoding)
+ return;
+ base.DefaultEncoding = value;
+ this.svlMapping.DefaultEncoding = value;
+ this.tslMapping.DefaultEncoding = value;
+ this.dvbMapping.DefaultEncoding = value;
+ this.ReparseNames();
+ }
+ }
+ #endregion
+
+ #region ReparseNames()
+ private void ReparseNames()
+ {
+ var nameLength = svlMapping.Settings.GetInt("NameLength");
+ var shortNameLength = dvbMapping.Settings.GetInt("ShortNameLength");
+ var dvbOffset = svlMapping.Settings.GetInt("BroadcastSystemData");
+
+ foreach (var list in this.DataRoot.ChannelLists)
+ {
+ if (list.IsMixedSourceFavoritesList)
+ continue;
+ foreach (var chan in list.Channels)
+ {
+ svlMapping.BaseOffset = chan.RawDataOffset;
+ chan.Name = ReadString(svlMapping, "Name", nameLength);
+
+ dvbMapping.BaseOffset = chan.RawDataOffset + dvbOffset;
+ chan.ShortName = ReadString(dvbMapping, "ShortName", shortNameLength);
+ }
+ }
+ }
+ #endregion
+}
diff --git a/source/ChanSort.Loader.Hisense/HisensePlugin.cs b/source/ChanSort.Loader.Hisense/HisensePlugin.cs
index e9ae9bb..7b4d9e7 100644
--- a/source/ChanSort.Loader.Hisense/HisensePlugin.cs
+++ b/source/ChanSort.Loader.Hisense/HisensePlugin.cs
@@ -7,7 +7,7 @@ namespace ChanSort.Loader.Hisense
{
public string DllName { get; set; }
public string PluginName => "Hisense (channel.db, servicelist.db)";
- public string FileFilter => "*.db";
+ public string FileFilter => "*.db;*.bin";
public SerializerBase CreateSerializer(string inputFile)
{
@@ -19,6 +19,8 @@ namespace ChanSort.Loader.Hisense
if (name.Contains("servicelist")) // models 2017 and later
return new ServicelistDb.ServicelistDbSerializer(inputFile);
+ if (name.StartsWith("his_") && name.EndsWith(".bin"))
+ return new HisBin.HisBinSerializer(inputFile);
return null;
}
}
diff --git a/source/ChanSort.sln b/source/ChanSort.sln
index ff16b8e..07ae1a0 100644
--- a/source/ChanSort.sln
+++ b/source/ChanSort.sln
@@ -105,7 +105,51 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChanSort.Loader.TCL", "Chan
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChanSort.Loader.DBM", "ChanSort.Loader.DBM\ChanSort.Loader.DBM.csproj", "{C1FF21EB-1CA6-4CE9-8BA8-9FAF71C5D6A6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.VisionEdge4K", "ChanSort.Loader.VisionEdge4K\ChanSort.Loader.VisionEdge4K.csproj", "{71ABCE5E-A833-4729-BC30-35FF0AE01DCA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChanSort.Loader.VisionEdge4K", "ChanSort.Loader.VisionEdge4K\ChanSort.Loader.VisionEdge4K.csproj", "{71ABCE5E-A833-4729-BC30-35FF0AE01DCA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HDD Hex Edit Neo", "HDD Hex Edit Neo", "{1BB3A27F-714C-42F0-A14E-8653B9AB8C24}"
+ ProjectSection(SolutionItems) = preProject
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\chansort.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\chansort.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\cvt_database-dat.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\cvt_database-dat.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\dbm.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\dbm.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\dtv_cmdb_1-bin.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\dtv_cmdb_1-bin.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\dtv_cmdb_2-bin.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\dtv_cmdb_2-bin.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\get_doc_size.js = Information\FileStructures_for_HHD_Hex_Editor_Neo\get_doc_size.js
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\his-svl.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\his-svl.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\LaSat_lst.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\LaSat_lst.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\panasonic_idtvChannelBin.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\panasonic_idtvChannelBin.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_chanLst-bin.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_chanLst-bin.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_dat.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_dat.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_472.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_472.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_476.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_476.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_480.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_mgr_480.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_SatelliteDb-bin.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\philips_SatelliteDb-bin.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Analog.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Analog.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Digital.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Digital.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Satellite.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-map-Satellite.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-SatDataBase.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-SatDataBase.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-TransponderDataBase.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\scm-TransponderDataBase.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-084-LP.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-084-LP.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-164_LH3000.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-164_LH3000.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-164_LH_LU_LF.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-164_LH_LU_LF.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-176-LD_LE_LK_LX_LW45_LW54.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-176-LD_LE_LK_LX_LW45_LW54.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-180-PT.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-180-PT.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-184-LH250C.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-184-LH250C.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-184-LV_LW_LK950S.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-184-LV_LW_LK950S.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-188-LM611S.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-188-LM611S.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-192-LM_LS.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-192-LM_LS.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-212-LT.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-212-LT.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-212-PN.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-212-PN.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-224-LN.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-224-LN.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-244-LB550U.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-244-LB550U.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LA.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LA.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LH.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LH.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LY.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-256-LY.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-260-LA.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-260-LA.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-280-LB580.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-280-LB580.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-common.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-common.h
+ Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-satellite.h = Information\FileStructures_for_HHD_Hex_Editor_Neo\tll-satellite.h
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -1328,6 +1372,9 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {1BB3A27F-714C-42F0-A14E-8653B9AB8C24} = {67AED502-8AEB-45F2-9B95-AC42B6A5D2C4}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BBF1F6CB-9445-4F6C-A69A-15C4DA246814}
EndGlobalSection
diff --git a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/chansort.h b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/chansort.h
index 3de7a07..9ec7774 100644
--- a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/chansort.h
+++ b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/chansort.h
@@ -35,3 +35,9 @@ enum ServiceType : byte
Option = 211
};
+
+function trim(str)
+{
+ var i = find(str, "\0");
+ return i < 0 ? str : substring(str, 0, i);
+}
diff --git a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/his-svl.h b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/his-svl.h
new file mode 100644
index 0000000..96e1fc6
--- /dev/null
+++ b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/his-svl.h
@@ -0,0 +1,299 @@
+#include "chansort.h"
+
+// common ---------------------------
+
+enum TableId : word
+{
+ Antenna = 1, Cable = 2, Sat = 3
+};
+
+struct Header
+{
+ word id;
+ char name[34];
+ dword size;
+};
+
+enum BroadcastType : byte
+{
+ Analog = 1,
+ Dvb = 2,
+};
+
+// TSL ------------------------------
+
+enum Medium : byte
+{
+ DigAnt = 1,
+ DigCable = 2,
+ DigSat = 3,
+ AnaAnt = 4,
+ AnaCable = 5,
+ AnaSat = 6
+};
+
+enum HisServiceType : byte
+{
+ TV = 1, Radio = 2, App = 3
+};
+
+enum BandwidthType { Unknown, Mhz6, Mhz7, Mhz8 };
+
+struct TslRecord
+{
+ var off0 = current_offset;
+ word id;
+
+ BroadcastType broadcastType;
+ Medium medium;
+ word nid;
+ word onid;
+ word tsid;
+ word nwlRecId;
+ byte nwlVal;
+ byte u1[3];
+
+ dword freq;
+ union
+ {
+ dword symRate;
+ BandwidthType bwType;
+ } bandwidth;
+ byte u2[192];
+ byte name[32];
+ var off1 = current_offset;
+ byte padding[tslRecordSize - (off1 - off0) - 12];
+ dword mask;
+ word nwlTblId;
+ word nwlRecId;
+ word satTblId;
+ word satRecId;
+};
+
+public struct HIS_TSL
+{
+ var tslRecordSize = 328;
+ Header tsl1;
+ Header tsl2;
+ Header tsl3;
+
+ var count = tsl1.size / tslRecordSize;
+ TslRecord antenna[count];
+
+ var count = tsl2.size / tslRecordSize;
+ TslRecord cable[count];
+
+ var count = tsl3.size / tslRecordSize;
+ TslRecord sat[count];
+};
+
+
+
+
+// SVL ------------------------------
+
+// bit values defined in https://patents.google.com/patent/CN102523534B/en
+
+enum NwMask : dword
+{
+ Skip = 1 << 3,
+ Fav1 = 1 << 4,
+ Fav2 = 1 << 5,
+ Fav3 = 1 << 6,
+ Fav4 = 1 << 7,
+ Lock = 1 << 8
+};
+
+enum OptionMask : dword
+{
+ Rename = 1 << 3,
+ Moved = 1 << 10
+};
+
+enum Option2Mask : dword
+{
+ Unknown = 0
+};
+
+enum Hashcode : dword
+{
+ Name = 0x0001,
+ ServiceId = 0x0002,
+ BroadcastType = 0x0004,
+ TlsRecId = 0x0008,
+ PrgNum = 0x0010,
+ ShortName = 0x0020,
+ Radio = 0x0400, // guess
+ Encrypted = 0x0800, // guess
+ Tv = 0x2000 // guess
+};
+
+enum DvbServiceRunningStatus : dword
+{
+ Unknown,
+ Undefined,
+ NotRunning,
+ StartInSeconds,
+ Pausing,
+ Running,
+ OffAir
+};
+
+enum LinkageMask : dword
+{
+ Nid = 0x01,
+ Onid = 0x02,
+ Tsid = 0x04
+};
+
+struct DvbId
+{
+ word onid;
+ word tsid;
+ word sid;
+};
+
+enum DvbReplacementMask: dword
+{
+ CaReplacement = 0x01,
+ ServiceReplacement = 0x02,
+ CmpltEitSch = 0x04,
+ EpgPid = 0x08
+};
+
+struct SvlDvb
+{
+ dword reserved;
+ byte shortName[18];
+ word caSystemId;
+ DvbServiceRunningStatus runningStatus;
+ DvbReplacementMask replacementMask;
+ DvbId caReplacement;
+ DvbId svcReplacement;
+ DvbId cmplEitSchOn;
+ DvbId hdSimulcastOn;
+ DvbId orig;
+ word epgPid;
+ bool freeCaMode;
+ byte ciHostShunningMode;
+
+ dword ciPlusPrevChannelId;
+ byte ciPlusCiTuneService;
+ byte nonStandard[10];
+
+ byte downmixMode;
+ byte subtitlePref1[4];
+ byte subtitlePref2[4];
+ qword svcTimeStarted;
+ qword svcTimeDuration;
+ word lastViewSvlTableId;
+ word lastViewSvlRecordId;
+ byte mfType;
+ bool eitPfFlag;
+ word curLcn;
+ word origLcn;
+ word lcnIndex;
+ word dvbcTsid;
+ word dvbcOnid;
+ byte audioIndex;
+ bool dvbsDpIdFlag;
+ word dvbsDpTsid;
+ word dvbsDpSid;
+ bool hbbtvOff;
+ byte sdtServiceType;
+ byte doNotScramble;
+ byte dadInfo[33];
+};
+
+[display(format("{0} - {1}", header.channelId.progNr, trim(header.name)))]
+struct SvlRecord
+{
+ // different sizes possible: 304 (observed), 308, and lots of smaller ones
+ var off0 = current_offset;
+ word recordId;
+ struct
+ {
+ dword nonStandard;
+ struct
+ {
+ word unk : 2;
+ word progNr : 14;
+ } channelId;
+ union
+ {
+ // !! file uses a different field order than documentation/definition !!
+ // Hisense seems to have accidentally put the hashcode and nwMask to the same location
+ // and there's indication that Skip is actually reversed (1=noskip)
+ Hashcode hashcode;
+ NwMask nwMask;
+ } hashcodeAndNwMask;
+ OptionMask optionMask;
+ Option2Mask option2Mask;
+ dword unknown;
+ word serviceId;
+ word tslTableId;
+ word tslRecordId;
+ word nwlTableId;
+ word nwlRecordId;
+ word satTableId;
+ word satRecordId;
+ BroadcastType broadcastType;
+ HisServiceType serviceType;
+ byte name[65];
+ byte nonStandard2[3];
+ byte customData[8];
+ byte privateData[20];
+ } header;
+
+ union
+ {
+ SvlDvb dvb;
+ byte padding[168];
+ } broadcastSystemData;
+ var off1 = current_offset;
+ byte padding[svlRecordSize - (off1 - off0)];
+};
+
+public struct HIS_SVL
+{
+ var svlRecordSize = 304;
+
+ Header svl1;
+ Header svl2;
+ Header svl3;
+
+ var count = svl1.size / svlRecordSize;
+ SvlRecord antenna[count];
+
+ var count = svl2.size / svlRecordSize;
+ SvlRecord cable[count];
+
+ var count = svl3.size / svlRecordSize;
+ SvlRecord sat[count];
+};
+
+
+
+// HIS_FAV -----------------------------------
+
+[display(format("{0} - {1}", trim(progNumDisplayInFavList), trim(name)))]
+struct FavRecord
+{
+ word svlTableId;
+ word svlRecordId;
+ char progNumDisplayInFavList[11];
+ char name[65];
+};
+
+public struct HIS_FAV
+{
+ dword fav1size;
+ dword fav2size;
+ dword fav3size;
+ dword fav4size;
+
+ FavRecord fav1[fav1size / sizeof(FavRecord)];
+ FavRecord fav2[fav2size / sizeof(FavRecord)];
+ FavRecord fav3[fav3size / sizeof(FavRecord)];
+ FavRecord fav4[fav4size / sizeof(FavRecord)];
+};
\ No newline at end of file