- Philips: added support for CM\_*.xml variant that uses a <ECSM> root element around the <ChannelMap>

- Philips: ability to read/write broken CM\_*.xml files that contain channel names with an unescaped & character
- Philips: enabled write mode for Repair\\FLASH\_\*/\*.db file format (one variant was confirmed to work)
  Favorite lists for this format are disabled for now (TV ignored them).
- Panasonic: importing a modified svl.bin file caused the TV to use case-sensitive sorting when using the
  function to list the names sorted alphabetically. This is now fixed.
This commit is contained in:
Horst Beham
2021-09-22 23:46:03 +02:00
parent 73d8f0c00f
commit 063ed165d2
9 changed files with 4231 additions and 31 deletions

View File

@@ -133,9 +133,6 @@ namespace ChanSort.Loader.Panasonic
{
byte[] buffer = new byte[100];
int len = (int)r.GetBytes(field["sname"], 0, buffer, 0, buffer.Length);
int end = Array.IndexOf<byte>(buffer, 0, 0, len);
if (end >= 0)
len = end;
this.RawName = new byte[len];
Array.Copy(buffer, 0, this.RawName, 0, len);
this.ChangeEncoding(encoding);
@@ -152,22 +149,25 @@ namespace ChanSort.Loader.Panasonic
// it can be code page encoded without any clue to what the code page is
// it can have DVB-control characters inside an UTF-8 stream
if (RawName.Length == 0)
int len = Array.IndexOf<byte>(this.RawName, 0, 0, this.RawName.Length);
if (len < 0)
len = this.RawName.Length;
if (len == 0)
return;
int startOffset;
int bytesPerChar;
if (!GetRecommendedEncoding(ref encoding, out startOffset, out bytesPerChar))
if (!GetRecommendedEncoding(ref encoding, out var startOffset, out var bytesPerChar))
return;
// single byte code pages might have UTF-8 code mixed in, so we have to parse it manually
StringBuilder sb = new StringBuilder();
this.NonAscii = false;
this.ValidUtf8 = true;
for (int i = startOffset; i < this.RawName.Length; i+=bytesPerChar)
for (int i = startOffset; i < len; i+=bytesPerChar)
{
byte c = this.RawName[i];
byte c2 = i + 1 < this.RawName.Length ? this.RawName[i + 1] : (byte)0;
byte c2 = i + 1 < len ? this.RawName[i + 1] : (byte)0;
byte c3 = i + 2 < len ? this.RawName[i + 2] : (byte)0;
byte c4 = i + 4 < len ? this.RawName[i + 3] : (byte)0;
if (c >= 0x80)
NonAscii = true;
@@ -178,10 +178,28 @@ namespace ChanSort.Loader.Panasonic
ValidUtf8 = false;
sb.Append((char) c);
}
else if (bytesPerChar == 1 && c >= 0xC0 && c <= 0xDF && c2 >= 0x80 && c2 <= 0xBF) // 2 byte UTF-8
else if (bytesPerChar == 1)
{
sb.Append((char)(((c & 0x1F) << 6) | (c2 & 0x3F)));
++i;
if (c >= 0xC0 && c <= 0xDF && c2 >= 0x80 && c2 <= 0xBF) // 2 byte UTF-8
{
sb.Append((char)(((c & 0x1F) << 6) | (c2 & 0x3F)));
++i;
}
else if (c >= 0xE0 && c <= 0xEF && (c2 & 0xC0) == 0x80 && (c3 & 0xC0) == 0x80) // 3 byte UTF-8
{
sb.Append((char)(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)));
i += 2;
}
else if (c >= 0xF0 && c <= 0xF7 && (c2 & 0xC0) == 0x80 && (c3 & 0xC0) == 0x80 && (c4 & 0xC0) == 0x80) // 4 byte UTF-8
{
sb.Append((char)(((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F)));
i += 3;
}
else
{
ValidUtf8 = false;
sb.Append(encoding.GetString(this.RawName, i, bytesPerChar));
}
}
else
{
@@ -190,8 +208,7 @@ namespace ChanSort.Loader.Panasonic
}
}
string longName, shortName;
this.GetChannelNames(sb.ToString(), out longName, out shortName);
this.GetChannelNames(sb.ToString(), out var longName, out var shortName);
this.Name = longName;
this.ShortName = shortName;
}

View File

@@ -20,7 +20,7 @@ namespace ChanSort.Loader.Panasonic
private string workFile;
private CypherMode cypherMode;
private byte[] fileHeader = new byte[0];
private byte[] fileHeader = Array.Empty<byte>();
private int dbSizeOffset;
private bool littleEndianByteOrder;
private string charEncoding;
@@ -329,11 +329,14 @@ order by s.ntype,major_channel
#region WriteChannels()
private void WriteChannels(SqliteCommand cmd, ChannelList channelList)
{
if (channelList.Channels.Count == 0)
return;
cmd.CommandText = "update SVL set major_channel=@progNr, sname=@sname, profile1index=@fav1, profile2index=@fav2, profile3index=@fav3, profile4index=@fav4, child_lock=@lock, skip=@skip where rowid=@rowid";
cmd.Parameters.Clear();
cmd.Parameters.Add("@rowid", SqliteType.Integer);
cmd.Parameters.Add("@progNr", SqliteType.Integer);
cmd.Parameters.Add("@sname", SqliteType.Blob);
cmd.Parameters.Add("@sname", this.implicitUtf8 ? SqliteType.Text : SqliteType.Blob); // must use "TEXT" when possible to preserve collation / case-insensitive sorting for the TV
cmd.Parameters.Add("@fav1", SqliteType.Integer);
cmd.Parameters.Add("@fav2", SqliteType.Integer);
cmd.Parameters.Add("@fav3", SqliteType.Integer);
@@ -351,7 +354,7 @@ order by s.ntype,major_channel
channel.UpdateRawData(this.explicitUtf8, this.implicitUtf8);
cmd.Parameters["@rowid"].Value = channel.RecordIndex;
cmd.Parameters["@progNr"].Value = channel.NewProgramNr;
cmd.Parameters["@sname"].Value = channel.RawName;
cmd.Parameters["@sname"].Value = this.implicitUtf8 ? channel.Name : channel.RawName; // must use a string when possible to preserve collation / case-insensitive sorting for the TV
for (int fav = 0; fav < 4; fav++)
cmd.Parameters["@fav" + (fav + 1)].Value = Math.Max(0, channel.GetPosition(fav+1));
cmd.Parameters["@lock"].Value = channel.Lock;