mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-01-15 20:02:04 +01:00
322 lines
12 KiB
C#
322 lines
12 KiB
C#
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using ChanSort.Api;
|
|
|
|
namespace ChanSort.Loader.Hisense.HisBin;
|
|
|
|
/*
|
|
* Loads Hisense HIS_DVB.BIN channel lists
|
|
*
|
|
* See also the his-dvb.h file in Information/FileStructures_for_HHD_Hex_Editor_Neo
|
|
*
|
|
* Some properties of these lists:
|
|
* - channel records are physically ordered by their program number
|
|
* - TV and radio are managed in separate lists, both starting at 1
|
|
* - channel and provider names are raw DVB strings including control bytes
|
|
*/
|
|
public class HisDvbBinSerializer : SerializerBase
|
|
{
|
|
private readonly ChannelList antTvChannels = new(SignalSource.DvbT | SignalSource.Tv, "Antenna TV");
|
|
private readonly ChannelList antRadioChannels = new(SignalSource.DvbT | SignalSource.Radio, "Antenna Radio");
|
|
private readonly ChannelList antDataChannels = new(SignalSource.DvbT | SignalSource.Data, "Antenna Data");
|
|
private readonly ChannelList cabTvChannels = new(SignalSource.DvbC | SignalSource.Tv, "Cable TV");
|
|
private readonly ChannelList cabRadioChannels = new(SignalSource.DvbC | SignalSource.Radio, "Cable Radio");
|
|
private readonly ChannelList cabDataChannels = new(SignalSource.DvbC | SignalSource.Data, "Cable Data");
|
|
private readonly ChannelList satTvChannels = new(SignalSource.DvbS | SignalSource.Tv, "Sat TV");
|
|
private readonly ChannelList satRadioChannels = new(SignalSource.DvbS | SignalSource.Radio, "Sat Radio");
|
|
private readonly ChannelList satDataChannels = new(SignalSource.DvbS | SignalSource.Data, "Sat Data");
|
|
|
|
private SubListInfo[] subListInfos;
|
|
|
|
private byte[] fileContent;
|
|
private int headerRecordSize, antRecordSize, cabRecordSize, satRecordSize;
|
|
|
|
private const string ERR_badFileFormat = "The content of the file doesn't match the expected format.";
|
|
|
|
private IniFile ini;
|
|
private DataMapping headerMapping, dvbMapping;
|
|
private DvbStringDecoder dvbStringDecoder;
|
|
|
|
#region class SubListInfo
|
|
private struct SubListInfo
|
|
{
|
|
public int Count;
|
|
public int Size;
|
|
public ChannelList TvList;
|
|
public ChannelList RadioList;
|
|
public ChannelList DataList;
|
|
public bool IsSat;
|
|
|
|
public SubListInfo(int count, int size, ChannelList tvList, ChannelList radioList, ChannelList dataList, bool isSat)
|
|
{
|
|
Count = count;
|
|
Size = size;
|
|
TvList = tvList;
|
|
RadioList = radioList;
|
|
DataList = dataList;
|
|
IsSat = isSat;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ctor()
|
|
public HisDvbBinSerializer(string inputFile) : base(inputFile)
|
|
{
|
|
this.Features.ChannelNameEdit = ChannelNameEditMode.None;
|
|
this.Features.CanSkipChannels = true;
|
|
this.Features.CanLockChannels = true;
|
|
this.Features.CanHideChannels = false;
|
|
this.Features.FavoritesMode = FavoritesMode.Flags;
|
|
this.Features.MaxFavoriteLists = 1;
|
|
this.Features.DeleteMode = DeleteMode.Physically;
|
|
this.Features.CanHaveGaps = true;
|
|
this.Features.AllowGapsInFavNumbers = false;
|
|
this.ReadConfigurationFromIniFile();
|
|
|
|
this.DataRoot.AddChannelList(antTvChannels);
|
|
this.DataRoot.AddChannelList(antRadioChannels);
|
|
this.DataRoot.AddChannelList(antDataChannels);
|
|
this.DataRoot.AddChannelList(cabTvChannels);
|
|
this.DataRoot.AddChannelList(cabRadioChannels);
|
|
this.DataRoot.AddChannelList(cabDataChannels);
|
|
this.DataRoot.AddChannelList(satTvChannels);
|
|
this.DataRoot.AddChannelList(satRadioChannels);
|
|
this.DataRoot.AddChannelList(satDataChannels);
|
|
foreach (var list in this.DataRoot.ChannelLists)
|
|
{
|
|
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ChannelOrTransponder));
|
|
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Encrypted));
|
|
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceType));
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ReadConfigurationFromIniFile()
|
|
|
|
private void ReadConfigurationFromIniFile()
|
|
{
|
|
string iniFile = this.GetType().Assembly.Location.ToLower().Replace(".dll", ".ini");
|
|
this.ini = new IniFile(iniFile);
|
|
this.headerMapping = new DataMapping(ini.GetSection("HIS_DVB.BIN"));
|
|
this.headerRecordSize = headerMapping.Settings.GetInt("HeaderSize");
|
|
|
|
this.dvbMapping = new DataMapping(ini.GetSection("HIS_DVB.BIN_Record"));
|
|
this.antRecordSize = this.dvbMapping.Settings.GetInt("RecordSizeDvbT");
|
|
this.cabRecordSize = this.dvbMapping.Settings.GetInt("RecordSizeDvbC");
|
|
this.satRecordSize = this.dvbMapping.Settings.GetInt("RecordSizeDvbS");
|
|
this.dvbMapping.DefaultEncoding = this.DefaultEncoding;
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Load()
|
|
|
|
public override void Load()
|
|
{
|
|
this.fileContent = File.ReadAllBytes(this.FileName);
|
|
|
|
this.headerMapping.SetDataPtr(this.fileContent, 0);
|
|
|
|
var antChannelCount = this.headerMapping.GetWord("NumChannelsDvbT");
|
|
var cabChannelCount = this.headerMapping.GetWord("NumChannelsDvbC");
|
|
var satChannelCount = this.headerMapping.GetWord("NumChannelsDvbS");
|
|
|
|
var expectedSize = headerRecordSize + antChannelCount * antRecordSize + cabChannelCount * cabRecordSize + satChannelCount * satRecordSize;
|
|
if (this.fileContent.Length != expectedSize)
|
|
throw new FileLoadException(ERR_badFileFormat);
|
|
|
|
this.dvbStringDecoder = new DvbStringDecoder(this.DefaultEncoding);
|
|
|
|
var nameOffset = dvbMapping.Settings.GetInt("Name");
|
|
var nameLength = dvbMapping.Settings.GetInt("NameLength");
|
|
var providerOffset = dvbMapping.Settings.GetInt("Provider");
|
|
var providerLength = dvbMapping.Settings.GetInt("ProviderLength");
|
|
|
|
this.subListInfos = new SubListInfo[]
|
|
{
|
|
new (antChannelCount, antRecordSize, antTvChannels, antRadioChannels, antDataChannels, false),
|
|
new (cabChannelCount, cabRecordSize, cabTvChannels, cabRadioChannels, cabDataChannels, false),
|
|
new (satChannelCount, satRecordSize, satTvChannels, satRadioChannels, satDataChannels, true)
|
|
};
|
|
|
|
var off = headerRecordSize;
|
|
foreach (var info in this.subListInfos)
|
|
{
|
|
for (int index = 0; index < info.Count; index++)
|
|
{
|
|
dvbMapping.SetDataPtr(fileContent, off);
|
|
var ci = ReadChannel(index, info.IsSat, nameOffset, nameLength, providerOffset, providerLength);
|
|
if (ci != null)
|
|
{
|
|
var src = ci.SignalSource & SignalSource.MaskTvRadioData;
|
|
var channels = src == SignalSource.Tv ? info.TvList : src == SignalSource.Radio ? info.RadioList : info.DataList;
|
|
this.DataRoot.AddChannel(channels, ci);
|
|
}
|
|
|
|
off += info.Size;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ReadChannel()
|
|
private ChannelInfo ReadChannel(int index, bool isSat, int nameOffset, int nameLength, int providerOffset, int providerLength)
|
|
{
|
|
ChannelInfo ci = new ChannelInfo(0, index, 0, "");
|
|
ci.RawDataOffset = dvbMapping.BaseOffset;
|
|
|
|
var type = dvbMapping.GetByte("TvRadioData");
|
|
if (type == 1)
|
|
ci.SignalSource |= SignalSource.Tv;
|
|
else if (type == 2)
|
|
ci.SignalSource |= SignalSource.Radio;
|
|
else
|
|
ci.SignalSource |= SignalSource.Data;
|
|
|
|
ci.Favorites = dvbMapping.GetByte("Fav") != 0 ? Favorites.A : 0;
|
|
ci.Skip = dvbMapping.GetByte("Skip") != 0;
|
|
ci.Lock = dvbMapping.GetByte("Lock") != 0;
|
|
ci.ServiceType = dvbMapping.GetByte("ServiceType");
|
|
ci.PcrPid = dvbMapping.GetWord("PcrPid");
|
|
ci.VideoPid = dvbMapping.GetWord("VideoPid");
|
|
ci.OldProgramNr = dvbMapping.GetWord("ProgNum");
|
|
ci.ServiceId = dvbMapping.GetWord("ServiceId");
|
|
ci.AudioPid = dvbMapping.GetWord("AudioPid");
|
|
|
|
this.dvbStringDecoder.GetChannelNames(fileContent, ci.RawDataOffset + nameOffset, nameLength, out var longName, out var shortName);
|
|
ci.Name = longName;
|
|
ci.ShortName = shortName;
|
|
|
|
this.dvbStringDecoder.GetChannelNames(fileContent, ci.RawDataOffset + providerOffset, providerLength, out var provider, out _);
|
|
ci.Provider = provider;
|
|
|
|
ci.TransportStreamId = dvbMapping.GetWord("Tsid");
|
|
ci.OriginalNetworkId = dvbMapping.GetWord("Onid");
|
|
ci.FreqInMhz = dvbMapping.GetDword("Frequency");
|
|
if (ci.FreqInMhz > 20000) // DVB-C/T has value in kHZ, DVB-S in MHz
|
|
ci.FreqInMhz /= 1000;
|
|
ci.SymbolRate = dvbMapping.GetWord("SymbolRate");
|
|
|
|
if (isSat)
|
|
ci.Satellite = dvbMapping.GetString("SatName", dvbMapping.Settings.GetInt("SatNameLength"));
|
|
|
|
return ci;
|
|
}
|
|
#endregion
|
|
|
|
|
|
// Saving ====================================
|
|
|
|
#region Save()
|
|
public override void Save()
|
|
{
|
|
using var mem = new MemoryStream(this.fileContent.Length);
|
|
using var writer = new BinaryWriter(mem);
|
|
writer.Write(this.fileContent, 0, this.headerRecordSize);
|
|
|
|
foreach (var info in this.subListInfos)
|
|
{
|
|
int newIndex = 0;
|
|
foreach (var list in new[] { info.TvList, info.RadioList, info.DataList })
|
|
{
|
|
var order = list.Channels.OrderBy(c => c, new DelegateComparer<ChannelInfo>(OrderChannelsComparer)).ToList();
|
|
|
|
foreach (var channel in order)
|
|
{
|
|
if (channel.IsDeleted)
|
|
continue;
|
|
|
|
// copy original data
|
|
var offset = writer.BaseStream.Position;
|
|
writer.Write(this.fileContent, channel.RawDataOffset, info.Size);
|
|
writer.Flush();
|
|
|
|
// prepare to overwrite with some new values
|
|
dvbMapping.SetDataPtr(mem.GetBuffer(), (int)offset);
|
|
dvbMapping.SetWord("ProgNum", channel.NewProgramNr);
|
|
dvbMapping.SetByte("Skip", channel.Skip ? 1 : 0);
|
|
dvbMapping.SetByte("Lock", channel.Lock ? 1 : 0);
|
|
dvbMapping.SetByte("Fav", channel.Favorites != 0 ? 1 : 0);
|
|
|
|
channel.RecordIndex = newIndex++;
|
|
channel.RawDataOffset = (int)offset;
|
|
}
|
|
|
|
// update number of channels in header
|
|
headerMapping.SetDataPtr(mem.GetBuffer(), 0);
|
|
headerMapping.SetDword("NumSatChannels", newIndex);
|
|
}
|
|
}
|
|
|
|
// write to file
|
|
this.fileContent = new byte[mem.Length];
|
|
Tools.MemCopy(mem.GetBuffer(), 0, this.fileContent, 0, (int)mem.Length);
|
|
File.WriteAllBytes(this.FileName, this.fileContent);
|
|
}
|
|
#endregion
|
|
|
|
#region OrderChannelsComparer()
|
|
private int OrderChannelsComparer(ChannelInfo a, ChannelInfo b)
|
|
{
|
|
// deleted channels to the end
|
|
if (a.NewProgramNr < 0)
|
|
return b.NewProgramNr == 0 ? a.RecordIndex.CompareTo(b.RecordIndex) : +1;
|
|
if (b.NewProgramNr < 0)
|
|
return -1;
|
|
|
|
return a.NewProgramNr.CompareTo(b.NewProgramNr);
|
|
}
|
|
#endregion
|
|
|
|
|
|
// Infrastructure ============================
|
|
|
|
#region DefaultEncoding
|
|
public override Encoding DefaultEncoding
|
|
{
|
|
get => base.DefaultEncoding;
|
|
set
|
|
{
|
|
if (value == this.DefaultEncoding)
|
|
return;
|
|
base.DefaultEncoding = value;
|
|
this.dvbMapping.DefaultEncoding = value;
|
|
|
|
if (this.dvbStringDecoder != null)
|
|
{
|
|
this.dvbStringDecoder.DefaultEncoding = value;
|
|
this.ReparseNames();
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ReparseNames()
|
|
private void ReparseNames()
|
|
{
|
|
var nameOffset = dvbMapping.Settings.GetInt("Name");
|
|
var nameLength = dvbMapping.Settings.GetInt("NameLength");
|
|
var providerOffset = dvbMapping.Settings.GetInt("Provider");
|
|
var providerLength = dvbMapping.Settings.GetInt("ProviderLength");
|
|
|
|
foreach (var list in this.DataRoot.ChannelLists)
|
|
{
|
|
if (list.IsMixedSourceFavoritesList)
|
|
continue;
|
|
foreach (var chan in list.Channels)
|
|
{
|
|
dvbMapping.BaseOffset = chan.RawDataOffset;
|
|
|
|
this.dvbStringDecoder.GetChannelNames(this.fileContent, chan.RawDataOffset + nameOffset, nameLength, out var longName, out var shortName);
|
|
chan.Name = longName;
|
|
chan.ShortName = shortName;
|
|
|
|
this.dvbStringDecoder.GetChannelNames(this.fileContent, chan.RawDataOffset + providerOffset, providerLength, out var provider, out _);
|
|
chan.Provider = provider;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|