diff --git a/source/ChanSort.Api/Model/ChannelList.cs b/source/ChanSort.Api/Model/ChannelList.cs index a042aeb..d355f0c 100644 --- a/source/ChanSort.Api/Model/ChannelList.cs +++ b/source/ChanSort.Api/Model/ChannelList.cs @@ -86,7 +86,7 @@ namespace ChanSort.Api string warning2 = null; bool isDupeProgNr = false; - if (ci.OldProgramNr != -1) + if (ci.OldProgramNr >= 0) { ChannelInfo other; this.channelByProgNr.TryGetValue(ci.OldProgramNr, out other); diff --git a/source/ChanSort.Api/Model/DataRoot.cs b/source/ChanSort.Api/Model/DataRoot.cs index 37a90aa..28925db 100644 --- a/source/ChanSort.Api/Model/DataRoot.cs +++ b/source/ChanSort.Api/Model/DataRoot.cs @@ -253,8 +253,13 @@ namespace ChanSort.Api chan.NewProgramNr = -1; } - for (int j=0; j<=this.FavListCount; j++) - chan.SetOldPosition(j, chan.GetPosition(j)); + for (int j = 0; j <= this.FavListCount; j++) + { + var newPos = chan.GetPosition(j); + chan.SetOldPosition(j, newPos); + if (newPos < -1) + chan.SetPosition(j, -1); + } } } } diff --git a/source/ChanSort.Api/Utils/IniFile.cs b/source/ChanSort.Api/Utils/IniFile.cs index 6e282db..256f103 100644 --- a/source/ChanSort.Api/Utils/IniFile.cs +++ b/source/ChanSort.Api/Utils/IniFile.cs @@ -18,26 +18,29 @@ namespace ChanSort.Api } #region Name - public string Name { get; private set; } + public string Name { get; } #endregion #region Set() - internal void Set(string key, string value) + public void Set(string key, object value) { - data[key] = value; + if (value == null) + data.Remove(key); + else + data[key] = value.ToString(); } #endregion #region Keys - public IEnumerable Keys { get { return data.Keys; } } + public IEnumerable Keys => data.Keys; + #endregion #region GetString() public string GetString(string key) { - string value; - if (!data.TryGetValue(key, out value)) + if (!data.TryGetValue(key, out var value)) return null; return value; } @@ -48,8 +51,7 @@ namespace ChanSort.Api public int GetInt(string key, int defaultValue = 0) { - string value; - if (!data.TryGetValue(key, out value)) + if (!data.TryGetValue(key, out var value)) return defaultValue; return this.ParseNumber(value); } @@ -60,11 +62,10 @@ namespace ChanSort.Api public byte[] GetBytes(string key) { - string value; - if (!data.TryGetValue(key, out value)) + if (!data.TryGetValue(key, out var value)) return null; if (string.IsNullOrEmpty(value)) - return new byte[0]; + return Array.Empty(); string[] parts = value.Split(','); byte[] bytes = new byte[parts.Length]; @@ -81,7 +82,7 @@ namespace ChanSort.Api { string value = this.GetString(key); if (string.IsNullOrEmpty(value)) - return new int[0]; + return Array.Empty(); string[] numbers = value.Split(','); int[] ret = new int[numbers.Length]; for (int i = 0; i < numbers.Length; i++) @@ -115,8 +116,8 @@ namespace ChanSort.Api try { return Convert.ToInt32(value, 16) * sig; } catch { return 0; } } - int intValue; - int.TryParse(value, out intValue); + + int.TryParse(value, out var intValue); return intValue; } #endregion @@ -133,10 +134,7 @@ namespace ChanSort.Api this.ReadIniFile(fileName); } - public IEnumerable
Sections - { - get { return this.sectionList; } - } + public IEnumerable
Sections => this.sectionList; public Section GetSection(string sectionName) { @@ -146,46 +144,44 @@ namespace ChanSort.Api #region ReadIniFile() private void ReadIniFile(string fileName) { - using (StreamReader rdr = new StreamReader(fileName)) + using StreamReader rdr = new StreamReader(fileName); + Section currentSection = null; + string line; + string key = null; + string val = null; + while ((line = rdr.ReadLine()) != null) { - Section currentSection = null; - string line; - string key = null; - string val = null; - while ((line = rdr.ReadLine()) != null) + string trimmedLine = line.Trim(); + if (trimmedLine.StartsWith(";")) + continue; + if (trimmedLine.StartsWith("[")) { - string trimmedLine = line.Trim(); - if (trimmedLine.StartsWith(";")) + string sectionName = trimmedLine.EndsWith("]") + ? trimmedLine.Substring(1, trimmedLine.Length - 2) + : trimmedLine.Substring(1); + currentSection = new Section(sectionName); + this.sectionList.Add(currentSection); + this.sectionDict[sectionName] = currentSection; + continue; + } + if (currentSection == null) + continue; + if (val == null) + { + int idx = trimmedLine.IndexOf("=", StringComparison.InvariantCulture); + if (idx < 0) continue; - if (trimmedLine.StartsWith("[")) - { - string sectionName = trimmedLine.EndsWith("]") - ? trimmedLine.Substring(1, trimmedLine.Length - 2) - : trimmedLine.Substring(1); - currentSection = new Section(sectionName); - this.sectionList.Add(currentSection); - this.sectionDict[sectionName] = currentSection; - continue; - } - if (currentSection == null) - continue; - if (val == null) - { - int idx = trimmedLine.IndexOf("="); - if (idx < 0) - continue; - key = trimmedLine.Substring(0, idx).Trim(); - val = trimmedLine.Substring(idx + 1).Trim(); - } - else - val += line; - if (val.EndsWith("\\")) - val = val.Substring(val.Length - 1).Trim(); - else - { - currentSection.Set(key, val); - val = null; - } + key = trimmedLine.Substring(0, idx).Trim(); + val = trimmedLine.Substring(idx + 1).Trim(); + } + else + val += line; + if (val.EndsWith("\\")) + val = val.Substring(val.Length - 1).Trim(); + else + { + currentSection.Set(key, val); + val = null; } } } diff --git a/source/ChanSort.Loader.CmdbBin/CmdbFileSerializer.cs b/source/ChanSort.Loader.CmdbBin/CmdbFileSerializer.cs index 3b3d8c5..db7607f 100644 --- a/source/ChanSort.Loader.CmdbBin/CmdbFileSerializer.cs +++ b/source/ChanSort.Loader.CmdbBin/CmdbFileSerializer.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; +using System.CodeDom; using System.IO; -using System.Linq; -using System.Reflection; using System.Text; using ChanSort.Api; @@ -12,17 +9,18 @@ namespace ChanSort.Loader.CmdbBin public class CmdbFileSerializer : SerializerBase { private IniFile ini; - private readonly MappingPool satMappings = new MappingPool("dtv_cmdb_2.bin"); private readonly ChannelList dvbsTv = new ChannelList(SignalSource.DvbS | SignalSource.Tv, "Sat TV"); private readonly ChannelList dvbsRadio = new ChannelList(SignalSource.DvbS | SignalSource.Radio, "Sat Radio"); private readonly ChannelList dvbsData = new ChannelList(SignalSource.DvbS | SignalSource.Radio, "Sat Data"); private DvbStringDecoder dvbStringDecoder; - private bool loaded = false; + private bool loaded; + private readonly StringBuilder protocol = new StringBuilder(); public CmdbFileSerializer(string inputFile) : base(inputFile) { this.Features.FavoritesMode = FavoritesMode.Flags; this.Features.MaxFavoriteLists = 1; + this.Features.DeleteMode = DeleteMode.FlagWithoutPrNr; // TODO there can be lots of channels in each list with number 65534, which seems to indicate user-deleted this.DataRoot.AddChannelList(dvbsTv); this.DataRoot.AddChannelList(dvbsRadio); @@ -30,31 +28,14 @@ namespace ChanSort.Loader.CmdbBin this.ReadConfigurationFromIniFile(); foreach (var list in this.DataRoot.ChannelLists) - { - //list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Favorites)); - //list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Skip)); - //list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Lock)); list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Hidden)); - //list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Encrypted)); - } } #region ReadConfigurationFromIniFile() - private void ReadConfigurationFromIniFile() { string iniFile = this.GetType().Assembly.Location.ToLowerInvariant().Replace(".dll", ".ini"); this.ini = new IniFile(iniFile); - - foreach (var section in ini.Sections) - { - int idx = section.Name.IndexOf(":"); - if (idx < 0) - continue; - string recordLength = idx < 0 ? "" : section.Name.Substring(idx + 1); - if (section.Name.StartsWith("dtv_cmdb_2")) - satMappings.AddMapping(recordLength, new DataMapping(section)); - } } #endregion @@ -63,14 +44,13 @@ namespace ChanSort.Loader.CmdbBin { this.dvbStringDecoder = new DvbStringDecoder(this.DefaultEncoding); - foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName))) + foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName) ?? ".")) { var lower = Path.GetFileName(file).ToLowerInvariant(); - var size = (int)new FileInfo(file).Length; switch (lower) { case "dtv_cmdb_2.bin": - LoadFile(file, this.dvbsTv, this.dvbsRadio, this.dvbsData, this.satMappings.GetMapping(size)); + LoadFile(file, this.dvbsTv, this.dvbsRadio, this.dvbsData); break; } } @@ -81,21 +61,22 @@ namespace ChanSort.Loader.CmdbBin #endregion #region LoadFile() - private void LoadFile(string file, ChannelList tvList, ChannelList radioList, ChannelList dataList, DataMapping fileMapping) + private void LoadFile(string file, ChannelList tvList, ChannelList radioList, ChannelList dataList) { var data = File.ReadAllBytes(file); - var sec = fileMapping.Settings; + var fileName = Path.GetFileName(file).ToLowerInvariant(); + var sec = this.ini.GetSection($"{fileName}:{data.Length}"); LoadBitmappedRecords(data, sec, "Satellite", ReadSatellite); LoadBitmappedRecords(data, sec, "Transponder", ReadTransponder); - LoadBitmappedRecords(data, sec, "Channel", (map, index) => ReadChannel(map, tvList, radioList, dvbsData, index)); + LoadBitmappedRecords(data, sec, "Channel", (map, index, len) => ReadChannel(map, tvList, radioList, dataList, index, len)); this.loaded = true; } #endregion #region LoadBitmappedRecords() - private void LoadBitmappedRecords(byte[] data, IniFile.Section sec, string recordType, Action readRecord) + private void LoadBitmappedRecords(byte[] data, IniFile.Section sec, string recordType, Action readRecord) { var lenRecord = sec.GetInt($"len{recordType}Record"); var map = new DataMapping(this.ini.GetSection($"dvbs{recordType}:{lenRecord}")); @@ -112,7 +93,7 @@ namespace ChanSort.Loader.CmdbBin for (byte mask = 1; mask != 0; mask <<= 1) { if ((b & mask) != 0) - readRecord(map, index); + readRecord(map, index, lenRecord); map.BaseOffset += lenRecord; if (++index >= count) break; @@ -122,7 +103,7 @@ namespace ChanSort.Loader.CmdbBin #endregion #region ReadSatellite() - private void ReadSatellite(DataMapping map, int index) + private void ReadSatellite(DataMapping map, int index, int lenRecord) { var sat = new Satellite(index); sat.Name = map.GetString("offName", map.Settings.GetInt("lenName")); @@ -131,7 +112,7 @@ namespace ChanSort.Loader.CmdbBin #endregion #region ReadTransponder() - private void ReadTransponder(DataMapping map, int index) + private void ReadTransponder(DataMapping map, int index, int lenRecord) { //var idx = map.GetWord("offTransponderIndex"); // seems to be some logical number, skipping a new numbers here and there @@ -147,7 +128,7 @@ namespace ChanSort.Loader.CmdbBin #endregion #region ReadChannel() - private void ReadChannel(DataMapping chanMap, ChannelList tvList, ChannelList radioList, ChannelList dataList, int recordIndex) + private void ReadChannel(DataMapping chanMap, ChannelList tvList, ChannelList radioList, ChannelList dataList, int recordIndex, int recordLength) { var channelType = (int)chanMap.GetByte("offChannelType"); if (channelType == 0) // some file format versions store the channel type in the upper nibble of a byte @@ -174,12 +155,12 @@ namespace ChanSort.Loader.CmdbBin var ch = new ChannelInfo(list.SignalSource, recordIndex, progNr, ""); ch.ServiceType = serviceType; - ch.ServiceTypeName = Api.LookupData.Instance.GetServiceTypeDescription(ch.ServiceType); - ch.PcrPid = chanMap.GetWord("offPcrPid") & 0x1FFF; + ch.ServiceTypeName = LookupData.Instance.GetServiceTypeDescription(ch.ServiceType); ch.ServiceId = chanMap.GetWord("offServiceId"); - ch.AudioPid = chanMap.GetWord("offAudioPid"); - ch.Encrypted = chanMap.GetFlag("Encrypted"); + 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; @@ -214,12 +195,73 @@ namespace ChanSort.Loader.CmdbBin } this.DataRoot.AddChannel(list, ch); + + + // validate checksum + var calculated = CalcChecksum(chanMap.Data, chanMap.BaseOffset, recordLength - 4); + var expected = BitConverter.ToInt32(chanMap.Data, chanMap.BaseOffset + recordLength - 4); + if (calculated != expected) + this.protocol.AppendFormat($"Data record has invalid checksum. Expected: {expected}, calculated: {calculated}\r\n"); } #endregion + #region CalcChecksum() + private int CalcChecksum(byte[] data, int offset, int length) + { + int sum = 0; + for (int i = 0; i < length; i++) + sum += data[offset++]; + return sum; + } + #endregion + + + #region Save() public override void Save(string tvOutputFile) { - throw new NotImplementedException(); + // save-as is not supported + + // TODO: currently hardcoded to support only dtv_cmdb_2.bin + + var data = File.ReadAllBytes(this.FileName); // filename is currently always the dtv_cmdb_2.bin, even if the user selected another file + var config = this.ini.GetSection("dtv_cmdb_2.bin:" + data.Length); + var lenChannelRecord = config.GetInt("lenChannelRecord"); + var sec = this.ini.GetSection($"dvbsChannel:{lenChannelRecord}"); + sec.Set("offChecksum", lenChannelRecord - 4); + var mapping = new DataMapping(sec); + + var baseOffset = config.GetInt("offChannelRecord"); + + foreach (var list in this.DataRoot.ChannelLists) + { + foreach (var chan in list.Channels) + { + mapping.SetDataPtr(data, baseOffset + (int)chan.RecordIndex * lenChannelRecord); + mapping.SetWord("offProgramNr", chan.IsDeleted ? 0xFFFE : chan.NewProgramNr); + if (chan.IsDeleted) // undo the automatic number changes from the "File / Save" function + { + chan.NewProgramNr = -2; + chan.IsDeleted = false; + } + + mapping.SetFlag("Skip", chan.Skip); + mapping.SetFlag("Lock", chan.Lock); + mapping.SetFlag("Fav", chan.Favorites != 0); + var sum = CalcChecksum(data, mapping.BaseOffset, lenChannelRecord - 4); + mapping.SetDword("offChecksum", sum); + } + } + + File.WriteAllBytes(this.FileName, data); } + #endregion + + + #region GetFileInformation() + public override string GetFileInformation() + { + return base.GetFileInformation() + "\n\n" + protocol; + } + #endregion } } diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index 3cfac50..80d3d2e 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -840,8 +840,7 @@ namespace ChanSort.Ui catch (IOException ex) { XtraMessageBox.Show(this, - Resources.MainForm_SaveFiles_ErrorMsg + - ex.Message, + Resources.MainForm_SaveFiles_ErrorMsg + "\n" + ex.Message, Resources.MainForm_SaveFiles_ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); } diff --git a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/dtv_cmdb_2-bin.h b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/dtv_cmdb_2-bin.h index 7a840dd..2a1f67f 100644 --- a/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/dtv_cmdb_2-bin.h +++ b/source/Information/FileStructures_for_HHD_Hex_Editor_Neo/dtv_cmdb_2-bin.h @@ -16,7 +16,7 @@ struct Flags { byte u1 : 3; byte protected : 1; - byte u2 : 1; + byte crypt : 1; byte skip : 1; byte locked : 1; byte u3 : 1; @@ -40,19 +40,20 @@ struct Channel_5048 word channelIndex; byte u1[11]; Flags flags; - byte u1b; + byte u2; ChannelType channelType; word serviceType; - byte u2[2]; + word fav; word transponderIndex; - word pmtPid; + DvbId pmtPid; word u3; - word PcrPid; - word u4[2]; + DvbId pcrPid; + DvbId videoPid; + word u4; word programNr; word serviceId; byte u5[22]; - word audioPid; + DvbId audioPid; byte u6[186]; char name[66]; char provider[270]; @@ -126,20 +127,20 @@ struct Channel_4532 word channelIndex; byte u1[11]; Flags flags; - byte u1b; + byte u2; ChannelType channelType; word serviceType; - byte u2[2]; + word fav; word transponderIndex; - word pmtPid; + DvbId pmtPid; word u3; - word pcrPid; - word videoPid; + DvbId pcrPid; + DvbId videoPid; word u4; word programNr; word serviceId; byte u5[22]; - word audioPid; + DvbId audioPid; byte u6[186]; char name[66]; char provider[270]; @@ -213,15 +214,15 @@ struct Channel_1684 word channelIndex; byte u1[11]; Flags flags; - byte u1b; + byte u2; ChannelType channelType; word serviceType; - byte u2[2]; + word fav; word transponderIndex; DvbId pmtPid; word u3; DvbId pcrPid; - DvbId videoPid_Maybe; + DvbId videoPid; word u4; word programNr; word serviceId; @@ -306,8 +307,8 @@ struct Channel_1322 word transponderIndex; DvbId pmtPid; byte u2[2]; - DvbId videoPid_maybe; - DvbId PcrPid; + DvbId pcrPid; + DvbId videoPid; word u4[2]; word programNr; word serviceId; @@ -384,13 +385,13 @@ struct Channel_1296 Flags flags_maybe; byte u1a; ChannelTypeNibble type; - byte u1b; + byte u2; byte serviceType; word transponderIndex; DvbId pmtPid; - byte u2[2]; + byte u3[2]; DvbId pcrPid; - DvbId videoPid_maybe; + DvbId videoPid; word u4; word programNr; word u4b; diff --git a/source/Test.Loader.Sony/SonyXmlTest.cs b/source/Test.Loader.Sony/SonyXmlTest.cs index e881cf3..143df96 100644 --- a/source/Test.Loader.Sony/SonyXmlTest.cs +++ b/source/Test.Loader.Sony/SonyXmlTest.cs @@ -38,7 +38,7 @@ namespace Test.Loader.Sony [TestMethod] public void TestKdlSatChannelsAddedToCorrectLists() { - this.TestChannelsAddedToCorrectLists("kdl_sdb-cable-sat.xml", SignalSource.DvbS, 1540, 1191, 173, 7216, "HUMAX DOWNLOAD SVC"); + this.TestChannelsAddedToCorrectLists("kdl_sdb-cable-sat.xml", SignalSource.DvbS, 1540, 1225, 173, 7216, "HUMAX DOWNLOAD SVC"); } [TestMethod] diff --git a/source/changelog.md b/source/changelog.md index 628e669..a2a91b8 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -18,6 +18,7 @@ ChanSort Change Log please send it to me via github or email. - Sony: Files with incorrect checksum are no longer rejected, as the TV seems to ignore bad checksums. Information about a bad checksum is visible under File / File Information. +- Sony: Sky option channels are now in the TV channel list rather than data channel list - updated Hungarian translation. Thanks to efi99 on Github! - pressing the "Del"-key on the keyboard no longer deletes a channel when a text editor is open - dragging a file (or something else) from outside ChanSort over the ChanSort window no longer creates an error