From c17a3eb0208f8cb3d7c8077c1e8e13676d70a893 Mon Sep 17 00:00:00 2001 From: Horst Beham Date: Wed, 18 Jan 2023 15:27:59 +0100 Subject: [PATCH] - re-added and reworked Hisense SVL_*.BIN loader (read-only atm) --- source/ChanSort.Api/Model/ChannelInfo.cs | 7 +- source/ChanSort.Api/Model/Transponder.cs | 2 + .../ChanSort.Loader.Hisense.csproj | 9 + .../ChanSort.Loader.Hisense.ini | 59 ++- .../HisBin/HisBinSerializer.cs | 432 ++++++++++++++++++ .../ChanSort.Loader.Hisense/HisensePlugin.cs | 4 +- source/ChanSort.sln | 49 +- .../chansort.h | 6 + .../his-svl.h | 299 ++++++++++++ 9 files changed, 839 insertions(+), 28 deletions(-) create mode 100644 source/ChanSort.Loader.Hisense/HisBin/HisBinSerializer.cs create mode 100644 source/Information/FileStructures_for_HHD_Hex_Editor_Neo/his-svl.h 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