mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-05-07 06:16:41 +02:00
- merged projects so that there is one loader for each manufacturer
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -35,6 +36,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -48,6 +50,7 @@
|
||||
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
|
||||
<CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
@@ -60,34 +63,30 @@
|
||||
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
|
||||
<CodeAnalysisFailOnMissingRules>true</CodeAnalysisFailOnMissingRules>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>..\DLL\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AnalogChannel.cs" />
|
||||
<Compile Include="DigitalChannel.cs" />
|
||||
<Compile Include="FavMode.cs" />
|
||||
<Compile Include="ModelConstants.cs" />
|
||||
<Compile Include="SamsungPlugin.cs" />
|
||||
<Compile Include="Scm\AnalogChannel.cs" />
|
||||
<Compile Include="Scm\DigitalChannel.cs" />
|
||||
<Compile Include="Scm\ModelConstants.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Resource.de.Designer.cs">
|
||||
<DependentUpon>Resource.de.resx</DependentUpon>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<Compile Include="Resource.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SatChannel.cs" />
|
||||
<Compile Include="SatelliteMapping.cs" />
|
||||
<Compile Include="ScmChannelBase.cs" />
|
||||
<Compile Include="ScmSerializer.cs" />
|
||||
<Compile Include="ScmSerializerPlugin.cs" />
|
||||
<Compile Include="Scm\SatChannel.cs" />
|
||||
<Compile Include="Scm\SatelliteMapping.cs" />
|
||||
<Compile Include="Scm\ScmChannelBase.cs" />
|
||||
<Compile Include="Scm\ScmSerializer.cs" />
|
||||
<Compile Include="Zip\DbChannel.cs" />
|
||||
<Compile Include="Zip\DbSerializer.cs" />
|
||||
<Compile Include="Zip\Utf16InsideUtf8EnvelopeEncoding.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ChanSort.Api\ChanSort.Api.csproj">
|
||||
@@ -100,18 +99,7 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resource.de.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.de.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
{
|
||||
class FavMode
|
||||
{
|
||||
}
|
||||
}
|
||||
63
source/ChanSort.Loader.Samsung/Resource.Designer.cs
generated
63
source/ChanSort.Loader.Samsung/Resource.Designer.cs
generated
@@ -1,63 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ChanSort.Loader.Samsung {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resource {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ChanSort.Loader.Samsung.Resource", typeof(Resource).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 1.3
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 1.3
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
24
source/ChanSort.Loader.Samsung/SamsungPlugin.cs
Normal file
24
source/ChanSort.Loader.Samsung/SamsungPlugin.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.IO;
|
||||
using ChanSort.Api;
|
||||
using ChanSort.Loader.Samsung.Scm;
|
||||
using ChanSort.Loader.Samsung.Zip;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
{
|
||||
public class SamsungPlugin : ISerializerPlugin
|
||||
{
|
||||
public string DllName { get; set; }
|
||||
public string PluginName => "Samsung (*.scm, *.zip)";
|
||||
public string FileFilter => "*.scm;*.zip";
|
||||
|
||||
public SerializerBase CreateSerializer(string inputFile)
|
||||
{
|
||||
var ext = Path.GetExtension(inputFile).ToLowerInvariant();
|
||||
if (ext == ".scm")
|
||||
return new ScmSerializer(inputFile);
|
||||
if (ext == ".zip")
|
||||
return new DbSerializer(inputFile);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
internal class AnalogChannel : ScmChannelBase
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
public class DigitalChannel : ScmChannelBase
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
public enum FavoritesIndexMode
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
class SatChannel : ScmChannelBase
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
internal class SatelliteMapping
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
public class ScmChannelBase : ChannelInfo
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
namespace ChanSort.Loader.Samsung.Scm
|
||||
{
|
||||
internal class ScmSerializer : SerializerBase
|
||||
{
|
||||
@@ -1,16 +0,0 @@
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung
|
||||
{
|
||||
public class ScmSerializerPlugin : ISerializerPlugin
|
||||
{
|
||||
public string DllName { get; set; }
|
||||
public string PluginName { get { return "Samsung B-H series"; } }
|
||||
public string FileFilter { get { return "*.scm"; } }
|
||||
|
||||
public SerializerBase CreateSerializer(string inputFile)
|
||||
{
|
||||
return new ScmSerializer(inputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
76
source/ChanSort.Loader.Samsung/Zip/DbChannel.cs
Normal file
76
source/ChanSort.Loader.Samsung/Zip/DbChannel.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung.Zip
|
||||
{
|
||||
internal class DbChannel : ChannelInfo
|
||||
{
|
||||
#region ctor()
|
||||
internal DbChannel(SQLiteDataReader r, IDictionary<string, int> field, DbSerializer loader, Dictionary<long, string> providers, Satellite sat, Transponder tp)
|
||||
{
|
||||
var chType = r.GetInt32(field["chType"]);
|
||||
this.SignalSource = DbSerializer.ChTypeToSignalSource(chType);
|
||||
|
||||
this.RecordIndex = r.GetInt64(field["SRV.srvId"]);
|
||||
this.OldProgramNr = r.GetInt32(field["major"]);
|
||||
this.FreqInMhz = (decimal)r.GetInt32(field["freq"]) / 1000;
|
||||
this.ChannelOrTransponder =
|
||||
(this.SignalSource & SignalSource.DvbT) == SignalSource.DvbT ? LookupData.Instance.GetDvbtTransponder(this.FreqInMhz).ToString() :
|
||||
(this.SignalSource & SignalSource.DvbC) == SignalSource.DvbC ? LookupData.Instance.GetDvbcTransponder(this.FreqInMhz).ToString() :
|
||||
(this.SignalSource & SignalSource.DvbS) == SignalSource.DvbS ? LookupData.Instance.GetAstraTransponder((int)this.FreqInMhz).ToString() :
|
||||
"";
|
||||
this.Name = loader.ReadUtf16(r, field["srvName"]);
|
||||
this.Hidden = r.GetBoolean(field["hidden"]);
|
||||
this.Encrypted = r.GetBoolean(field["scrambled"]);
|
||||
this.Lock = r.GetBoolean(field["lockMode"]);
|
||||
this.Skip = !r.GetBoolean(field["numSel"]);
|
||||
|
||||
if (sat != null)
|
||||
{
|
||||
this.Satellite = sat.Name;
|
||||
this.SatPosition = sat.OrbitalPosition;
|
||||
}
|
||||
if (tp != null)
|
||||
{
|
||||
this.Transponder = tp;
|
||||
this.SymbolRate = tp.SymbolRate;
|
||||
this.Polarity = tp.Polarity;
|
||||
}
|
||||
|
||||
if ((this.SignalSource & SignalSource.Digital) != 0)
|
||||
this.ReadDvbData(r, field, loader, providers);
|
||||
else
|
||||
this.ReadAnalogData(r, field);
|
||||
|
||||
base.IsDeleted = this.OldProgramNr == -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ReadAnalogData()
|
||||
private void ReadAnalogData(SQLiteDataReader r, IDictionary<string, int> field)
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadDvbData()
|
||||
protected void ReadDvbData(SQLiteDataReader r, IDictionary<string, int> field, DbSerializer loader, Dictionary<long, string> providers)
|
||||
{
|
||||
this.ShortName = loader.ReadUtf16(r, field["srvName"]);
|
||||
this.RecordOrder = r.GetInt32(field["major"]);
|
||||
int serviceType = r.GetInt32(field["srvType"]);
|
||||
this.ServiceType = serviceType;
|
||||
this.SignalSource |= LookupData.Instance.IsRadioTvOrData(serviceType);
|
||||
this.OriginalNetworkId = r.GetInt32(field["onid"]);
|
||||
this.TransportStreamId = r.GetInt32(field["tsid"]);
|
||||
this.ServiceId = r.GetInt32(field["progNum"]);
|
||||
this.VideoPid = r.GetInt32(field["vidPid"]);
|
||||
if (!r.IsDBNull(field["provId"]))
|
||||
this.Provider = providers.TryGet(r.GetInt64(field["provId"]));
|
||||
this.AddDebug(r.GetInt32(field["lcn"]).ToString());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
643
source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs
Normal file
643
source/ChanSort.Loader.Samsung/Zip/DbSerializer.cs
Normal file
@@ -0,0 +1,643 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Samsung.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Loader for Samsung J/K/M/N/R/Q series .zip files (2015 - 2020)
|
||||
/// </summary>
|
||||
class DbSerializer : SerializerBase
|
||||
{
|
||||
private readonly Dictionary<long, DbChannel> channelById = new Dictionary<long, DbChannel>();
|
||||
private readonly Dictionary<ChannelList, string> dbPathByChannelList = new Dictionary<ChannelList, string>();
|
||||
private readonly List<string> tableNames = new List<string>();
|
||||
private Encoding encoding;
|
||||
|
||||
private enum FileType { Unknown, SatDb, ChannelDbDvb, ChannelDbAnalog, ChannelDbIp }
|
||||
|
||||
#region ctor()
|
||||
public DbSerializer(string inputFile) : base(inputFile)
|
||||
{
|
||||
DepencencyChecker.AssertVc2010RedistPackageX86Installed();
|
||||
|
||||
this.Features.ChannelNameEdit = ChannelNameEditMode.All;
|
||||
this.Features.DeleteMode = DeleteMode.Physically;
|
||||
this.Features.CanSkipChannels = true;
|
||||
this.Features.CanLockChannels = true;
|
||||
this.Features.CanHideChannels = true;
|
||||
this.Features.SupportedFavorites = Favorites.A | Favorites.B | Favorites.C | Favorites.D | Favorites.E;
|
||||
this.Features.SortedFavorites = true;
|
||||
this.Features.AllowGapsInFavNumbers = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Load()
|
||||
public override void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.UnzipFileToTempFolder();
|
||||
if (File.Exists(this.TempPath + "\\sat"))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var conn = new SQLiteConnection("Data Source=" + this.TempPath + "\\sat"))
|
||||
{
|
||||
conn.Open();
|
||||
this.ReadSatDatabase(conn);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(this.TempPath, "*.");
|
||||
if (files.Length == 0)
|
||||
throw new FileLoadException("The Samsung .zip channel list archive does not contain any supported files.");
|
||||
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var filename = Path.GetFileName(filePath) ?? "";
|
||||
if (filename.StartsWith("vconf_"))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
using (var conn = new SQLiteConnection("Data Source=" + filePath))
|
||||
{
|
||||
FileType type;
|
||||
conn.Open();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
this.RepairCorruptedDatabaseImage(cmd);
|
||||
type = this.DetectFileType(cmd);
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FileType.SatDb:
|
||||
break;
|
||||
case FileType.ChannelDbAnalog:
|
||||
case FileType.ChannelDbDvb:
|
||||
case FileType.ChannelDbIp:
|
||||
ReadChannelDatabase(conn, filePath, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// force closing the file and releasing the locks
|
||||
SQLiteConnection.ClearAllPools();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RepairCorruptedDatabaseImage()
|
||||
private void RepairCorruptedDatabaseImage(SQLiteCommand cmd)
|
||||
{
|
||||
cmd.CommandText = "REINDEX";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DetectFileType()
|
||||
private FileType DetectFileType(SQLiteCommand cmd)
|
||||
{
|
||||
this.tableNames.Clear();
|
||||
cmd.CommandText = "select name from sqlite_master where type='table'";
|
||||
using var r = cmd.ExecuteReader();
|
||||
while (r.Read())
|
||||
this.tableNames.Add(r.GetString(0).ToUpper());
|
||||
|
||||
if (tableNames.Contains("SAT") && tableNames.Contains("SAT_TP"))
|
||||
return FileType.SatDb;
|
||||
|
||||
if (tableNames.Contains("CHNL") && tableNames.Contains("SRV") && tableNames.Contains("SRV_DVB"))
|
||||
return FileType.ChannelDbDvb;
|
||||
|
||||
if (tableNames.Contains("CHNL") && tableNames.Contains("SRV") && tableNames.Contains("SRV_ANL"))
|
||||
return FileType.ChannelDbAnalog;
|
||||
|
||||
if (tableNames.Contains("CHNL") && tableNames.Contains("SRV") && tableNames.Contains("SRV_IP"))
|
||||
return FileType.ChannelDbIp;
|
||||
|
||||
return FileType.Unknown;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadSatDatabase()
|
||||
private void ReadSatDatabase(SQLiteConnection conn)
|
||||
{
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
this.RepairCorruptedDatabaseImage(cmd);
|
||||
this.ReadSatellites(cmd);
|
||||
this.ReadTransponders(cmd);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadSatellites()
|
||||
private void ReadSatellites(SQLiteCommand cmd)
|
||||
{
|
||||
cmd.CommandText = "select distinct satId, cast(satName as blob), satPos, satDir from SAT";
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
while (r.Read())
|
||||
{
|
||||
Satellite sat = new Satellite(r.GetInt32(0));
|
||||
int pos = Math.Abs(r.GetInt32(2));
|
||||
// 171027 - ohuseyinoglu: For user-defined satellites, the direction may be -1
|
||||
// (and not just 1 for "E", 0 for "W")
|
||||
int dir = r.GetInt32(3);
|
||||
sat.OrbitalPosition = $"{pos / 10}.{pos % 10}{(dir == 1 ? "E" : dir == 0 ? "W" : "")}";
|
||||
sat.Name = ReadUtf16(r, 1);
|
||||
this.DataRoot.AddSatellite(sat);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadTransponders()
|
||||
private void ReadTransponders(SQLiteCommand cmd)
|
||||
{
|
||||
cmd.CommandText = "select satId, tpFreq, tpPol, tpSr, tpId from SAT_TP";
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
while (r.Read())
|
||||
{
|
||||
// 171027 - ohuseyinoglu: tpId is the primary key of this table, we should be able to use it as "id/dict. index"
|
||||
// It will also be our lookup value for the CHNL table
|
||||
int id = r.GetInt32(4);
|
||||
Transponder tp = new Transponder(id);
|
||||
tp.FrequencyInMhz = (decimal)r.GetInt32(1) / 1000;
|
||||
tp.Number = id;
|
||||
tp.Polarity = r.GetInt32(2) == 0 ? 'H' : 'V';
|
||||
tp.Satellite = this.DataRoot.Satellites.TryGet(r.GetInt32(0));
|
||||
tp.SymbolRate = r.GetInt32(3);
|
||||
this.DataRoot.AddTransponder(tp.Satellite, tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region ReadChannelDatabase()
|
||||
private void ReadChannelDatabase(SQLiteConnection conn, string dbPath, FileType fileType)
|
||||
{
|
||||
this.channelById.Clear();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
var providers = fileType == FileType.ChannelDbDvb ? this.ReadProviders(cmd) : null;
|
||||
var channelList = this.ReadChannels(cmd, dbPath, providers, fileType);
|
||||
this.ReadFavorites(cmd);
|
||||
this.dbPathByChannelList.Add(channelList, dbPath);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadProviders()
|
||||
private Dictionary<long, string> ReadProviders(SQLiteCommand cmd)
|
||||
{
|
||||
var dict = new Dictionary<long, string>();
|
||||
try
|
||||
{
|
||||
cmd.CommandText = "select provId, cast(provName as blob) from PROV";
|
||||
var prevEncoding = this.encoding;
|
||||
this.encoding = Encoding.BigEndianUnicode; // while Sat and Service names might be utf16 binary data inside an utf8 envelope, the providers are always plain utf16
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
while (r.Read())
|
||||
dict.Add(r.GetInt64(0), ReadUtf16(r, 1));
|
||||
}
|
||||
|
||||
this.encoding = prevEncoding;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadChannels()
|
||||
private ChannelList ReadChannels(SQLiteCommand cmd, string dbPath, Dictionary<long, string> providers, FileType fileType)
|
||||
{
|
||||
var signalSource = DetectSignalSource(cmd, fileType);
|
||||
|
||||
string name = Path.GetFileName(dbPath);
|
||||
ChannelList channelList = this.CreateChannelList(signalSource, name);
|
||||
string table = fileType == FileType.ChannelDbDvb ? "SRV_DVB" : fileType == FileType.ChannelDbAnalog ? "SRV_ANL" : "SRV_IP";
|
||||
List<string> fieldNames = new List<string> {
|
||||
"chType", "chNum", "freq", // CHNL
|
||||
"SRV.srvId", "major", "progNum", "cast(srvName as blob) srvName", "srvType", "hidden", "scrambled", "lockMode", "numSel", "elim" // SRV
|
||||
};
|
||||
if (fileType == FileType.ChannelDbDvb)
|
||||
fieldNames.AddRange(new[] {"onid", "tsid", "vidPid", "provId", "cast(shrtSrvName as blob) shrtSrvName", "lcn"}); // SRV_DVB
|
||||
|
||||
var sql = this.BuildQuery(table, fieldNames);
|
||||
var fields = this.GetFieldMap(fieldNames);
|
||||
|
||||
cmd.CommandText = sql;
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
int prevNr = 0;
|
||||
while (r.Read())
|
||||
{
|
||||
if (r.GetInt32(fields["elim"]) != 0)
|
||||
continue;
|
||||
|
||||
// 171027 - ohuseyinoglu: With our change in transponder indexing, we can directly look it up by "chNum" now!
|
||||
var tp = this.DataRoot.Transponder.TryGet(r.GetInt32(1));
|
||||
// ... and get the satellite from that transponder - if set
|
||||
// Note that we can have channels from multiple satellites in the same list, so this is a loop variable now
|
||||
var sat = tp?.Satellite;
|
||||
var channel = new DbChannel(r, fields, this, providers, sat, tp);
|
||||
|
||||
if (channel.OldProgramNr == prevNr) // when there is a SRV_EXT_APP table in the database, the service with the highest ext_app "recState" takes priority
|
||||
continue;
|
||||
|
||||
this.DataRoot.AddChannel(channelList, channel);
|
||||
this.channelById.Add(channel.RecordIndex, channel);
|
||||
prevNr = channel.OldProgramNr;
|
||||
}
|
||||
}
|
||||
|
||||
this.DataRoot.AddChannelList(channelList);
|
||||
return channelList;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CreateChannelList()
|
||||
private ChannelList CreateChannelList(SignalSource signalSource, string name)
|
||||
{
|
||||
var list = new ChannelList(signalSource, name);
|
||||
if ((list.SignalSource & SignalSource.IP) != 0)
|
||||
{
|
||||
list.VisibleColumnFieldNames = new List<string>
|
||||
{
|
||||
"OldPosition", "Position", "PrNr", "Name", "Favorites", "SymbolRate"
|
||||
};
|
||||
}
|
||||
return list;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DetectSignalSource()
|
||||
private static SignalSource DetectSignalSource(SQLiteCommand cmd, FileType fileType)
|
||||
{
|
||||
if (fileType == FileType.ChannelDbIp)
|
||||
return SignalSource.IP|SignalSource.Digital;
|
||||
var signalSource = fileType == FileType.ChannelDbAnalog ? SignalSource.Analog : SignalSource.Digital;
|
||||
cmd.CommandText = "select distinct chType from CHNL";
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
if (r.Read())
|
||||
{
|
||||
var ss = ChTypeToSignalSource(r.GetInt32(0));
|
||||
if (ss != 0)
|
||||
signalSource = ss;
|
||||
}
|
||||
}
|
||||
return signalSource;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ChTypeToSignalSource()
|
||||
internal static SignalSource ChTypeToSignalSource(int chType)
|
||||
{
|
||||
switch (chType)
|
||||
{
|
||||
case 1: return SignalSource.AnalogT;
|
||||
case 2: return SignalSource.DvbT;
|
||||
case 3: return SignalSource.AnalogC;
|
||||
case 4: return SignalSource.DvbC;
|
||||
case 7: return SignalSource.DvbS;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BuildQuery()
|
||||
private string BuildQuery(string table, IList<string> fieldNames)
|
||||
{
|
||||
string sql = "select ";
|
||||
for (int i = 0; i < fieldNames.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sql += ",";
|
||||
sql += fieldNames[i];
|
||||
}
|
||||
sql += " from " + table + " inner join SRV on SRV.srvId="+table+".srvId inner join CHNL on CHNL.chId=SRV.chId";
|
||||
|
||||
if (this.tableNames.Contains("SRV_EXT_APP")) // in format 1352.0 there are duplicate "major" values in SRV and this recState seems to be the only difference
|
||||
sql += " inner join SRV_EXT_APP on SRV_EXT_APP.srvId=SRV.srvId order by SRV.major, ifnull(SRV_EXT_APP.recState,0) desc";
|
||||
|
||||
return sql;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetFieldMap()
|
||||
private IDictionary<string, int> GetFieldMap(IList<string> fieldNames)
|
||||
{
|
||||
Dictionary<string, int> field = new Dictionary<string, int>();
|
||||
for (int i = 0; i < fieldNames.Count; i++)
|
||||
{
|
||||
var idx = fieldNames[i].LastIndexOf(' ') + 1;
|
||||
field[fieldNames[i].Substring(idx)] = i;
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadFavorites()
|
||||
private void ReadFavorites(SQLiteCommand cmd)
|
||||
{
|
||||
cmd.CommandText = "select srvId, fav, pos from SRV_FAV";
|
||||
var r = cmd.ExecuteReader();
|
||||
int favPosAdjust = tableNames.Contains("SRV_EXT_APP") ? 0 : 1;
|
||||
while (r.Read())
|
||||
{
|
||||
var channel = this.channelById.TryGet(r.GetInt64(0));
|
||||
if (channel == null)
|
||||
continue;
|
||||
int fav = r.GetInt32(1) - 1; // fav values start with 1 in the table
|
||||
int pos = r.GetInt32(2) + favPosAdjust; // pos values start with 0 or 1
|
||||
if (pos >= 0)
|
||||
{
|
||||
channel.Favorites |= (Favorites) (1 << fav);
|
||||
channel.OldFavIndex[fav] = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadUtf16()
|
||||
internal string ReadUtf16(SQLiteDataReader r, int fieldIndex)
|
||||
{
|
||||
if (r.IsDBNull(fieldIndex))
|
||||
return null;
|
||||
byte[] nameBytes = new byte[200];
|
||||
int nameLen = (int)r.GetBytes(fieldIndex, 0, nameBytes, 0, nameBytes.Length);
|
||||
this.encoding ??= AutoDetectUtf16Encoding(nameBytes, nameLen);
|
||||
if (this.encoding == null)
|
||||
return string.Empty;
|
||||
|
||||
return encoding.GetString(nameBytes, 0, nameLen).Replace("\0", ""); // remove trailing \0 characters found in Samsung "_T_..." channel list
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AutoDetectUtf16Endian()
|
||||
private Encoding AutoDetectUtf16Encoding(byte[] nameBytes, int nameLen)
|
||||
{
|
||||
if (this.DefaultEncoding is UnicodeEncoding)
|
||||
return this.DefaultEncoding;
|
||||
|
||||
int evenBytesZero = 0;
|
||||
int oddBytesZero = 0;
|
||||
int bytesAbove128 = 0;
|
||||
for (int i = 0; i < nameLen; i += 2)
|
||||
{
|
||||
if (nameBytes[i] == 0)
|
||||
++evenBytesZero;
|
||||
if (nameBytes[i] >= 128)
|
||||
++bytesAbove128;
|
||||
if (nameBytes[i + 1] == 0)
|
||||
++oddBytesZero;
|
||||
if (nameBytes[i + 1] >= 128)
|
||||
++bytesAbove128;
|
||||
}
|
||||
|
||||
if (evenBytesZero + oddBytesZero == nameLen)
|
||||
return null;
|
||||
|
||||
if (bytesAbove128 + 1 >= nameLen)
|
||||
{
|
||||
//this.Features.ChannelNameEdit = ChannelNameEditMode.None; // unclear if the encoder produces byte sequences that the TV can decode again
|
||||
return new Utf16InsideUtf8EnvelopeEncoding();
|
||||
}
|
||||
|
||||
return evenBytesZero >= oddBytesZero ? Encoding.BigEndianUnicode : Encoding.Unicode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DefaultEncoding
|
||||
public override Encoding DefaultEncoding
|
||||
{
|
||||
get => base.DefaultEncoding;
|
||||
set
|
||||
{
|
||||
if (!(value is UnicodeEncoding))
|
||||
return;
|
||||
|
||||
var oldEncoding = base.DefaultEncoding;
|
||||
if (oldEncoding != null)
|
||||
{
|
||||
// change encoding of channel names
|
||||
foreach (var list in this.DataRoot.ChannelLists)
|
||||
{
|
||||
foreach (var chan in list.Channels)
|
||||
{
|
||||
byte[] bytes;
|
||||
if (chan.Name != null)
|
||||
{
|
||||
bytes = oldEncoding.GetBytes(chan.Name);
|
||||
chan.Name = value.GetString(bytes);
|
||||
}
|
||||
if (chan.ShortName != null)
|
||||
{
|
||||
bytes = oldEncoding.GetBytes(chan.ShortName);
|
||||
chan.ShortName = value.GetString(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
base.DefaultEncoding = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Save()
|
||||
public override void Save(string tvOutputFile)
|
||||
{
|
||||
foreach (var channelList in this.DataRoot.ChannelLists)
|
||||
{
|
||||
var dbPath = this.dbPathByChannelList[channelList];
|
||||
SaveChannelList(channelList, dbPath);
|
||||
}
|
||||
|
||||
// force closing the file and releasing the locks
|
||||
SQLiteConnection.ClearAllPools();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
this.ZipToOutputFile(tvOutputFile);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SaveChannelList()
|
||||
private void SaveChannelList(ChannelList channelList, string dbPath)
|
||||
{
|
||||
using var conn = new SQLiteConnection("Data Source=" + dbPath);
|
||||
conn.Open();
|
||||
using var cmdUpdateSrv = PrepareUpdateCommand(conn);
|
||||
using var cmdDeleteSrv = PrepareDeleteCommand(conn, (channelList.SignalSource & SignalSource.Digital) != 0);
|
||||
using var cmdInsertFav = PrepareInsertFavCommand(conn);
|
||||
using var cmdUpdateFav = PrepareUpdateFavCommand(conn);
|
||||
using var cmdDeleteFav = PrepareDeleteFavCommand(conn);
|
||||
using (var trans = conn.BeginTransaction())
|
||||
{
|
||||
Editor.SequentializeFavPos(channelList, 5);
|
||||
this.WriteChannels(cmdUpdateSrv, cmdDeleteSrv, cmdInsertFav, cmdUpdateFav, cmdDeleteFav, channelList);
|
||||
trans.Commit();
|
||||
}
|
||||
this.RepairCorruptedDatabaseImage(cmdUpdateSrv);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Prepare*Command()
|
||||
|
||||
private SQLiteCommand PrepareUpdateCommand(SQLiteConnection conn)
|
||||
{
|
||||
var canUpdateNames = this.Features.ChannelNameEdit != ChannelNameEditMode.None;
|
||||
var cmd = conn.CreateCommand();
|
||||
var updateSrvName = canUpdateNames ? ", srvName=cast(@srvname as varchar)" : "";
|
||||
cmd.CommandText = "update SRV set major=@nr, lockMode=@lock, hideGuide=@hidden, hidden=@hidden, numSel=@numsel" + updateSrvName + " where srvId=@id";
|
||||
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int64));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@nr", DbType.Int32));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@lock", DbType.Boolean));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@hidden", DbType.Boolean));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@numsel", DbType.Boolean));
|
||||
if (canUpdateNames)
|
||||
cmd.Parameters.Add(new SQLiteParameter("@srvname", DbType.Binary));
|
||||
cmd.Prepare();
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private SQLiteCommand PrepareDeleteCommand(SQLiteConnection conn, bool digital)
|
||||
{
|
||||
var cmd = conn.CreateCommand();
|
||||
var sql = new StringBuilder();
|
||||
cmd.CommandText = "select name from sqlite_master where sql like '%srvId integer%' order by name desc";
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
while (r.Read())
|
||||
sql.AppendLine($"; delete from {r.GetString(0)} where srvId=@id");
|
||||
}
|
||||
cmd.CommandText = sql.ToString();
|
||||
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int64));
|
||||
cmd.Prepare();
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private SQLiteCommand PrepareInsertFavCommand(SQLiteConnection conn)
|
||||
{
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "insert into SRV_FAV (srvId, fav, pos) values (@id, @fav, @pos)";
|
||||
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int64));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@fav", DbType.Int32));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@pos", DbType.Int32));
|
||||
cmd.Prepare();
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private SQLiteCommand PrepareUpdateFavCommand(SQLiteConnection conn)
|
||||
{
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "update SRV_FAV set pos=@pos where srvId=@id and fav=@fav";
|
||||
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int64));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@fav", DbType.Int32));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@pos", DbType.Int32));
|
||||
cmd.Prepare();
|
||||
return cmd;
|
||||
}
|
||||
private SQLiteCommand PrepareDeleteFavCommand(SQLiteConnection conn)
|
||||
{
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "delete from SRV_FAV where srvId=@id and fav=@fav";
|
||||
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int64));
|
||||
cmd.Parameters.Add(new SQLiteParameter("@fav", DbType.Int32));
|
||||
cmd.Prepare();
|
||||
return cmd;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WriteChannels()
|
||||
private void WriteChannels(SQLiteCommand cmdUpdateSrv, SQLiteCommand cmdDeleteSrv, SQLiteCommand cmdInsertFav, SQLiteCommand cmdUpdateFav, SQLiteCommand cmdDeleteFav,
|
||||
ChannelList channelList, bool analog = false)
|
||||
{
|
||||
bool canUpdateNames = this.Features.ChannelNameEdit != ChannelNameEditMode.None;
|
||||
foreach (ChannelInfo channelInfo in channelList.Channels.ToList())
|
||||
{
|
||||
var channel = channelInfo as DbChannel;
|
||||
if (channel == null) // ignore reference list proxy channels
|
||||
continue;
|
||||
|
||||
if (channel.IsDeleted)
|
||||
{
|
||||
// delete channel from all tables that have a reference to srvId
|
||||
cmdDeleteSrv.Parameters["@id"].Value = channel.RecordIndex;
|
||||
cmdDeleteSrv.ExecuteNonQuery();
|
||||
channelList.Channels.Remove(channelInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
// update channel record
|
||||
cmdUpdateSrv.Parameters["@id"].Value = channel.RecordIndex;
|
||||
cmdUpdateSrv.Parameters["@nr"].Value = channel.NewProgramNr;
|
||||
cmdUpdateSrv.Parameters["@lock"].Value = channel.Lock;
|
||||
cmdUpdateSrv.Parameters["@hidden"].Value = channel.Hidden;
|
||||
cmdUpdateSrv.Parameters["@numsel"].Value = !channel.Skip;
|
||||
if (canUpdateNames)
|
||||
cmdUpdateSrv.Parameters["@srvname"].Value = channel.Name == null ? (object)DBNull.Value : encoding.GetBytes(channel.Name);
|
||||
cmdUpdateSrv.ExecuteNonQuery();
|
||||
|
||||
// update favorites
|
||||
for (int i=0, mask=1; i<5; i++, mask <<= 1)
|
||||
{
|
||||
int oldPos = channel.OldFavIndex[i];
|
||||
int newPos = ((int)channel.Favorites & mask) != 0 ? channel.FavIndex[i] : -1;
|
||||
|
||||
if (newPos >= 0)
|
||||
{
|
||||
var c = oldPos < 0 ? cmdInsertFav : cmdUpdateFav;
|
||||
c.Parameters["@id"].Value = channel.RecordIndex;
|
||||
c.Parameters["@fav"].Value = i + 1;
|
||||
c.Parameters["@pos"].Value = newPos - 1;
|
||||
c.ExecuteNonQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdDeleteFav.Parameters["@id"].Value = channel.RecordIndex;
|
||||
cmdDeleteFav.Parameters["@fav"].Value = i + 1;
|
||||
cmdDeleteFav.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
channel.FavIndex[i] = newPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ChanSort.Loader.Samsung.Zip
|
||||
{
|
||||
// Samsung 1242 format does not store UTF16 characters directly, but instead wraps 16 data bits inside a UTF-8 lead + continuation byte sequence.
|
||||
// A 3 byte UTF-8 sequence is used to encode 16 bits of utf-16 big endian input: 1110aaaa 10bbbbcc 10ccdddd represents the 16bit big endian integer ccccddddaaaabbbb, i.e. 0xE4, 0x84, 0x80 => 0x00, 0x41 => "A" in UTF-16 BE
|
||||
// The Samsung encoder seems to create some illegal UTF-8 sequences at the end of the string as a result of padding and operating on 32bit inputs (2 characters) with big-endianness, which
|
||||
// this decoder has to take care of. 0xFFFD can appear both in the raw input bytes (0xFF, 0xFB) as well as already encoded into UTF-8 wrappings (0xEF,0xBF,0xBD)
|
||||
|
||||
// This implementation here decodes the UTF-8 byte sequence into UTF-16 Little Endian for the sake of simplicity: aaaa=4, bbbb=1, cccc=0, dddd=0 => 0xE4, 0x84, 0x80 => 0x41, 0x00 => "A" in UTF-16 LE.
|
||||
// The encoder here operates on 16bit characters and not 32bit 2-characters, so there is no need for padding and no invalid UTF-8 sequences.
|
||||
|
||||
public class Utf16InsideUtf8EnvelopeEncoding : Encoding
|
||||
{
|
||||
public override int GetMaxByteCount(int charCount)
|
||||
{
|
||||
return charCount * 3;
|
||||
}
|
||||
|
||||
public override int GetByteCount(char[] chars, int index, int count)
|
||||
{
|
||||
return count * 3;
|
||||
}
|
||||
|
||||
public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
|
||||
{
|
||||
var utf16Le = Unicode.GetBytes(chars, charIndex, charCount);
|
||||
int o = byteIndex;
|
||||
int c = utf16Le.Length;
|
||||
for (int i = 0; i < c; i += 2, o += 3)
|
||||
{
|
||||
var b0 = utf16Le[i + 0];
|
||||
var b1 = utf16Le[i + 1];
|
||||
bytes[o + 0] = (byte) (0xE0 + (b0 >> 4));
|
||||
bytes[o + 1] = (byte) (0x80 + ((b0 & 0x0F) << 2) + (b1 >> 6));
|
||||
bytes[o + 2] = (byte) (0x80 + (b1 & 0x3F));
|
||||
}
|
||||
|
||||
return charCount * 3;
|
||||
}
|
||||
|
||||
|
||||
public override int GetMaxCharCount(int byteCount)
|
||||
{
|
||||
return (byteCount + 2) / 3;
|
||||
}
|
||||
|
||||
public override int GetCharCount(byte[] bytes, int index, int count)
|
||||
{
|
||||
return (count + 2) / 3;
|
||||
}
|
||||
|
||||
public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
|
||||
{
|
||||
using MemoryStream ms = new MemoryStream(40);
|
||||
for (int i = byteIndex, c = byteIndex + byteCount; i < c; i++)
|
||||
{
|
||||
int b0 = bytes[i + 0];
|
||||
if (b0 == 0 && i == c - 1) // satellite names end with a single trailing 0x00 byte
|
||||
break;
|
||||
if (b0 > 0xF7) // invalid UTF-8 lead byte. (0xFF, 0xFD) = 0xFFFD in BigEndian can appear unencoded at the end of the byte stream, likely as a padding
|
||||
continue;
|
||||
if (b0 >= 0xE0) // 3-byte UTF envelope for 2 input bytes
|
||||
{
|
||||
int b1 = bytes[i + 1];
|
||||
int b2 = bytes[i + 2];
|
||||
if ((b2 & 0xC0) != 0x80) // invalid 2nd UTF-8 continuation byte; only a single byte is encoded as 1110aaaa 10bbbbcc => aaaabbbb
|
||||
{
|
||||
b2 = 0;
|
||||
--i;
|
||||
}
|
||||
int ch1 = ((b0 & 0x0F) << 4) | ((b1 & 0x3C) >> 2);
|
||||
int ch2 = ((b1 & 0x03) << 6) | (b2 & 0x3F);
|
||||
if (ch1 != 0xFF || ch2 != 0xFD) // ignore UTF-16 "replacement character" U-0xFFFD
|
||||
{
|
||||
ms.WriteByte((byte) ch1);
|
||||
ms.WriteByte((byte) ch2);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
else if (b0 >= 0xC0) // 2-byte UTF envelope for 1 input byte as 110xaaaa 10bbbbcc => aaaabbbb
|
||||
{
|
||||
int b1 = bytes[i + 1];
|
||||
int ch = ((b0 & 0x0F) << 4) | ((b1 & 0x3C)>>2);
|
||||
ms.WriteByte((byte)ch);
|
||||
ms.WriteByte(0);
|
||||
i++;
|
||||
}
|
||||
else if (b0 < 0x80) // 1-byte UTF envelope for 1 input byte < 0x80
|
||||
{
|
||||
ms.WriteByte(bytes[i]);
|
||||
ms.WriteByte(0);
|
||||
}
|
||||
}
|
||||
|
||||
return Encoding.Unicode.GetChars(ms.GetBuffer(), 0, (int) ms.Length, chars, charIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user