using System.Collections.Generic; using System.IO; using System.Reflection; using System.Linq; using System.Windows.Forms; using ChanSort.Api; using ICSharpCode.SharpZipLib.Zip; namespace ChanSort.Loader.ScmFile { class ScmSerializer : SerializerBase { public struct ChannelAndFreq { public int Channel; public decimal Frequency; public ChannelAndFreq(int ch, decimal freq) { this.Channel = ch; this.Frequency = freq; } } private readonly Dictionary modelConstants = new Dictionary(); private readonly MappingPool analogMappings = new MappingPool("Analog"); private readonly MappingPool dvbctMappings = new MappingPool("DVB-C/T"); private readonly MappingPool dvbsMappings = new MappingPool("DVB-S"); private readonly MappingPool hdplusMappings = new MappingPool("AstraHDPlus"); private readonly MappingPool analogFineTuneMappings = new MappingPool("FineTune"); private readonly MappingPool ptccableMappings = new MappingPool("PTC"); private readonly MappingPool transponderMappings = new MappingPool("TransponderDataBase"); private readonly ChannelList avbtChannels = new ChannelList(SignalSource.AnalogT, SignalType.Mixed); private readonly ChannelList avbcChannels = new ChannelList(SignalSource.AnalogC, SignalType.Mixed); private readonly ChannelList dvbcChannels = new ChannelList(SignalSource.DvbC, SignalType.Mixed); private readonly ChannelList dvbtChannels = new ChannelList(SignalSource.DvbT, SignalType.Mixed); private readonly ChannelList dvbsChannels = new ChannelList(SignalSource.DvbS, SignalType.Mixed); private readonly ChannelList hdplusChannels = new ChannelList(SignalSource.HdPlusD, SignalType.Mixed); private readonly Dictionary avbtFrequency = new Dictionary(); private readonly Dictionary avbcFrequency = new Dictionary(); private readonly Dictionary dvbcFrequency = new Dictionary(); private readonly Dictionary dvbtFrequency = new Dictionary(); private byte[] avbtFileContent; private byte[] avbcFileContent; private byte[] dvbtFileContent; private byte[] dvbcFileContent; private byte[] dvbsFileContent; private byte[] hdplusFileContent; private ModelConstants c; #region ctor() public ScmSerializer(string inputFile) : base(inputFile) { this.ReadConfigurationFromIniFile(); } #endregion #region DisplayName public override string DisplayName { get { return "Samsung *.scm Loader"; } } #endregion #region ReadConfigurationFromIniFile() private void ReadConfigurationFromIniFile() { string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini"); IniFile ini = new IniFile(iniFile); foreach (var section in ini.Sections) { int idx = section.Name.IndexOf(":"); int len=0; if (idx >= 0) int.TryParse(section.Name.Substring(idx + 1), out len); if (section.Name.StartsWith("Series:")) modelConstants.Add(section.Name, new ModelConstants(section)); else if (section.Name.StartsWith("Analog:")) analogMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("DvbCT:")) dvbctMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("DvbS:")) dvbsMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("FineTune:")) analogFineTuneMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("AstraHDPlusD:")) hdplusMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("TransponderDataBase.dat:")) transponderMappings.AddMapping(len, new DataMapping(section)); else if (section.Name.StartsWith("PTC:")) ptccableMappings.AddMapping(len, new DataMapping(section)); } } #endregion #region Load() public override void Load() { using (ZipFile zip = new ZipFile(this.FileName)) { DetectModelConstants(zip); DataRoot.SupportedFavorites = c.supportedFavorites; ReadAnalogFineTuning(zip); ReadAnalogChannels(zip, "map-AirA", this.avbtChannels, out this.avbtFileContent, this.avbtFrequency); ReadAnalogChannels(zip, "map-CableA", this.avbcChannels, out this.avbcFileContent, this.avbcFrequency); ReadDvbTransponderFrequenciesFromPtc(zip, "PTCAIR", this.dvbtFrequency); ReadDvbctChannels(zip, "map-AirD", this.dvbtChannels, out this.dvbtFileContent, this.dvbtFrequency); ReadDvbTransponderFrequenciesFromPtc(zip, "PTCCABLE", this.dvbcFrequency); ReadDvbctChannels(zip, "map-CableD", this.dvbcChannels, out this.dvbcFileContent, this.dvbcFrequency); ReadSatellites(zip); ReadTransponder(zip, "TransponderDataBase.dat"); ReadTransponder(zip, "UserTransponderDataBase.dat"); ReadDvbsChannels(zip); ReadAstraHdPlusChannels(zip); } } #endregion #region DetectModelConstants() private void DetectModelConstants(ZipFile zip) { if (DetectModelFromCloneInfoFile(zip)) return; if (DetectModelFromContentFileLengths(zip)) return; if (DetectModelFromFileName()) return; throw new IOException("Unable to determine TV model from file content or name"); } #endregion #region DetectModelFromFileName() private bool DetectModelFromFileName() { string file = Path.GetFileName(this.FileName); System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("channel_list_[A-Z]{2}[0-9]{2}([A-Z])[0-9A-Z]+_[0-9]+\\.scm"); var match = regex.Match(file); if (match.Success) { string series = match.Groups[1].Value; if (this.modelConstants.TryGetValue("Series:" + series, out this.c)) return true; } return false; } #endregion #region DetectModelFromCloneInfoFile() private bool DetectModelFromCloneInfoFile(ZipFile zip) { byte[] cloneInfo = ReadFileContent(zip, "CloneInfo"); if (cloneInfo == null) { this.c = this.modelConstants["Series:B"]; return true; } if (cloneInfo.Length >= 9) { char series = (char) cloneInfo[8]; if (this.modelConstants.TryGetValue("Series:" + series, out this.c)) return true; } return false; } #endregion #region DetectModelFromContentFileLengths() private bool DetectModelFromContentFileLengths(ZipFile zip) { string[] candidates = new[] { DetectModelFromAirAOrCableA(zip), DetectModelFromAirDOrCableD(zip), DetectModelFromSateD(zip), DetectModelFromTranspoderDatabase(zip) }; string validCandidates = "BCDE"; foreach (var candidateList in candidates) { if (candidateList == null) continue; string newValidCandidats = ""; foreach (var candidate in candidateList) { if (validCandidates.Contains(candidate)) newValidCandidats += candidate; } validCandidates = newValidCandidats; } if (validCandidates.Length != 1) return false; this.modelConstants.TryGetValue("Series:" + validCandidates, out this.c); return true; } #endregion #region DetectModelFromAirAOrCableA() private string DetectModelFromAirAOrCableA(ZipFile zip) { var entry = zip.GetEntry("map-AirA") ?? zip.GetEntry("map-CableA"); if (entry == null) return null; var candidates = ""; if (entry.Size % 28000 == 0) candidates += "B"; if (entry.Size % 40000 == 0) candidates += "C"; if (entry.Size % 64000 == 0) candidates += "DE"; return candidates; } #endregion #region DetectModelFromAirDOrCableD() private string DetectModelFromAirDOrCableD(ZipFile zip) { var entry = zip.GetEntry("map-AirD") ?? zip.GetEntry("map-CableD"); if (entry == null) return null; var candidates = ""; if (entry.Size % 248 == 0) candidates += "B"; if (entry.Size % 292 == 0) candidates += "C"; if (entry.Size % 320 == 0) candidates += "DE"; return candidates; } #endregion #region DetectModelFromSateD() private string DetectModelFromSateD(ZipFile zip) { var entry = zip.GetEntry("map-SateD"); if (entry == null) return null; var candidates = ""; if (entry.Size % 144 == 0) candidates += "BC"; if (entry.Size % 172 == 0) candidates += "D"; if (entry.Size % 168 == 0) candidates += "E"; return candidates; } #endregion #region DetectModelFromTranspoderDatabase() private string DetectModelFromTranspoderDatabase(ZipFile zip) { var entry = zip.GetEntry("TransponderDatabase.dat"); if (entry == null) return null; var size = entry.Size - 4; var candidates = ""; if (size%49 == 0) candidates += "B"; if (size%45 == 0) candidates += "CDE"; return candidates; } #endregion #region ReadAnalogFineTuning() private void ReadAnalogFineTuning(ZipFile zip) { int entrySize = c.avbtFineTuneLength; if (entrySize == 0) return; byte[] data = ReadFileContent(zip, "FineTune"); if (data == null) return; var mapping = analogFineTuneMappings.GetMapping(c.avbtFineTuneLength); mapping.SetDataPtr(data, 0); int count = data.Length / c.avbtFineTuneLength; for (int i = 0; i < count; i++) { bool isCable = mapping.GetFlag("offIsCable", "maskIsCable"); // HACK: this is just a guess int slot = mapping.GetWord("offSlotNr"); float freq = mapping.GetFloat("offFrequency"); var dict = isCable ? avbcFrequency : avbtFrequency; dict[slot] = (decimal)freq; mapping.BaseOffset += c.avbtFineTuneLength; } } #endregion #region ReadAnalogChannels() private void ReadAnalogChannels(ZipFile zip, string fileName, ChannelList list, out byte[] data, Dictionary freq) { data = null; int entrySize = c.avbtChannelLength; if (entrySize == 0) return; data = ReadFileContent(zip, fileName); if (data == null) return; this.DataRoot.AddChannelList(list); var rawChannel = analogMappings.GetMapping(entrySize); rawChannel.SetDataPtr(data, 0); int count = data.Length / entrySize; for (int slotIndex = 0; slotIndex < count; slotIndex++) { MapAnalogChannel(rawChannel, slotIndex, list, freq.TryGet(slotIndex)); rawChannel.BaseOffset += entrySize; } } #endregion #region MapAnalogChannel() private void MapAnalogChannel(DataMapping rawChannel, int slotIndex, ChannelList list, decimal freq) { AnalogChannel ci = new AnalogChannel(slotIndex, list.SignalSource, rawChannel, freq); if (!ci.InUse) return; this.DataRoot.AddChannel(list, ci); } #endregion #region ReadDvbTransponderFrequenciesFromPtc() private void ReadDvbTransponderFrequenciesFromPtc(ZipFile zip, string file, IDictionary table) { byte[] data = ReadFileContent(zip, file); if (data == null) return; var mapping = ptccableMappings.GetMapping(c.ptcLength); mapping.SetDataPtr(data, 0); int count = data.Length / c.ptcLength; for (int i = 0; i < count; i++) { int transp = mapping.GetWord("offChannelTransponder"); float freq = mapping.GetFloat("offFrequency"); table[transp] = (decimal)freq; mapping.BaseOffset += c.ptcLength; } } #endregion #region ReadDvbctChannels() private void ReadDvbctChannels(ZipFile zip, string fileName, ChannelList list, out byte[] data, Dictionary frequency) { data = null; int entrySize = c.dvbtChannelLength; if (entrySize == 0) return; data = ReadFileContent(zip, fileName); if (data == null) return; this.DataRoot.AddChannelList(list); DataMapping rawChannel = dvbctMappings.GetMapping(entrySize); rawChannel.SetDataPtr(data, 0); int count = data.Length / entrySize; for (int slotIndex = 0; slotIndex < count; slotIndex++) { DigitalChannel ci = new DigitalChannel(slotIndex, list.SignalSource, rawChannel, frequency); if (ci.OldProgramNr != 0) this.DataRoot.AddChannel(list, ci); rawChannel.BaseOffset += entrySize; } } #endregion #region ReadSatellites() private void ReadSatellites(ZipFile zip) { byte[] data = ReadFileContent(zip, "SatDataBase.dat"); if (data == null) return; SatelliteMapping satMapping = new SatelliteMapping(data, 4); int count = data.Length/this.c.dvbsSatelliteLength; for (int i = 0; i < count; i++) { if (satMapping.MagicMarker != 0x55) throw new IOException("Unknown SatDataBase.dat format"); string location = string.Format("{0}{1}", satMapping.Longitude, satMapping.IsEast ? "E" : "W"); Satellite satellite = new Satellite(satMapping.SatelliteNr); satellite.Name = satMapping.Name; satellite.OrbitalPosition = location; this.DataRoot.Satellites.Add(satMapping.SatelliteNr, satellite); satMapping.BaseOffset += this.c.dvbsSatelliteLength; } } #endregion #region ReadTransponder() private void ReadTransponder(ZipFile zip, string fileName) { byte[] data = ReadFileContent(zip, fileName); if (data == null) return; int count = (data.Length-4)/c.dvbsTransponderLength; var mapping = this.transponderMappings.GetMapping(c.dvbsTransponderLength); for (int i=0; i