[Symbian Installation File] Parse Symbian OS >= 6.0 conditions.

This commit is contained in:
2023-10-08 03:16:55 +01:00
parent 15e1924640
commit dccfa1c4ad
6 changed files with 623 additions and 14 deletions

View File

@@ -0,0 +1,274 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Symbian.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Symbian plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Symbian installer (.sis) packages and shows information.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Text;
namespace Aaru.Archives;
public sealed partial class Symbian
{
ConditionalExpression ParseConditionalExpression(BinaryReader br, uint maxOffset, StringBuilder sb,
ref Attribute? attribute)
{
if(br.BaseStream.Position >= maxOffset)
return null;
var type = (ConditionalType)br.ReadUInt32();
var operatorString = "";
SubConditionalExpression subExpression;
TwoSubsConditionalExpression twoSubsConditionalExpression;
switch(type)
{
case ConditionalType.Equals:
if(type == ConditionalType.Equals)
operatorString = " == ";
twoSubsConditionalExpression = new TwoSubsConditionalExpression
{
type = type
};
sb.Append("(");
twoSubsConditionalExpression.leftOperand = ParseConditionalExpression(br, maxOffset, sb, ref attribute);
sb.Append(operatorString);
twoSubsConditionalExpression.rightOperand =
ParseConditionalExpression(br, maxOffset, sb, ref attribute);
sb.Append(")");
attribute = null;
return twoSubsConditionalExpression;
case ConditionalType.Differs:
operatorString = " != ";
goto case ConditionalType.Equals;
case ConditionalType.GreaterThan:
operatorString = " > ";
goto case ConditionalType.Equals;
case ConditionalType.LessThan:
operatorString = " < ";
goto case ConditionalType.Equals;
case ConditionalType.GreaterOrEqualThan:
operatorString = " >= ";
goto case ConditionalType.Equals;
case ConditionalType.LessOrEqualThan:
operatorString = " <= ";
goto case ConditionalType.Equals;
case ConditionalType.And:
operatorString = " && ";
goto case ConditionalType.Equals;
case ConditionalType.Or:
operatorString = " || ";
goto case ConditionalType.Equals;
case ConditionalType.Exists:
sb.Append("exists(");
subExpression = new SubConditionalExpression
{
type = type,
subExpression = ParseConditionalExpression(br, maxOffset, sb, ref attribute)
};
sb.Append(")");
attribute = null;
return subExpression;
case ConditionalType.DeviceCapability:
sb.Append("devcap(");
subExpression = new SubConditionalExpression
{
type = type,
subExpression = ParseConditionalExpression(br, maxOffset, sb, ref attribute)
};
sb.Append(')');
attribute = null;
return subExpression;
case ConditionalType.ApplicationCapability:
twoSubsConditionalExpression = new TwoSubsConditionalExpression
{
type = type
};
sb.Append("appcap(");
twoSubsConditionalExpression.leftOperand = ParseConditionalExpression(br, maxOffset, sb, ref attribute);
sb.Append(", ");
twoSubsConditionalExpression.rightOperand =
ParseConditionalExpression(br, maxOffset, sb, ref attribute);
sb.Append(')');
attribute = null;
return twoSubsConditionalExpression;
case ConditionalType.Not:
sb.Append('!');
subExpression = new SubConditionalExpression
{
type = type,
subExpression = ParseConditionalExpression(br, maxOffset, sb, ref attribute)
};
attribute = null;
return subExpression;
case ConditionalType.String:
var stringExpression = new StringConditionalExpression
{
type = type,
length = br.ReadUInt32(),
pointer = br.ReadUInt32()
};
long position = br.BaseStream.Position;
br.BaseStream.Seek(stringExpression.pointer, SeekOrigin.Begin);
byte[] buffer = br.ReadBytes((int)stringExpression.length);
stringExpression.@string = _encoding.GetString(buffer);
br.BaseStream.Seek(position, SeekOrigin.Begin);
sb.Append($"\"{stringExpression.@string}\"");
attribute = null;
break;
case ConditionalType.Attribute:
var attributeExpression = new AttributeConditionalExpression
{
type = type,
attribute = (Attribute)br.ReadUInt32(),
unused = br.ReadUInt32()
};
sb.Append($"{attributeExpression.attribute}");
attribute = attributeExpression.attribute;
return attributeExpression;
case ConditionalType.Number:
var numberExpression = new NumberConditionalExpression
{
type = type,
number = br.ReadUInt32(),
unused = br.ReadUInt32()
};
switch(attribute)
{
case Attribute.Manufacturer:
sb.Append($"{(ManufacturerCode)numberExpression.number}");
break;
case Attribute.ManufacturerHardwareRev:
case Attribute.ManufacturerSoftwareRev:
case Attribute.DeviceFamilyRev:
sb.Append($"{numberExpression.number >> 8}.{numberExpression.number & 0xFF}");
break;
case Attribute.MachineUid:
sb.Append($"{DecodeMachineUid(numberExpression.number)}");
break;
case Attribute.DeviceFamily:
sb.Append($"{(DeviceFamilyCode)numberExpression.number}");
break;
case Attribute.CPU:
sb.Append($"{(CpuCode)numberExpression.number}");
break;
case Attribute.CPUArch:
sb.Append($"{(CpuArchitecture)numberExpression.number}");
break;
case Attribute.CPUABI:
sb.Append($"{(CpuAbiCode)numberExpression.number}");
break;
case Attribute.CPUSpeed:
sb.Append($"{numberExpression.number / 1024}MHz");
break;
case Attribute.SystemTickPeriod:
sb.Append($"{numberExpression.number}μs");
break;
case Attribute.MemoryRAM:
case Attribute.MemoryRAMFree:
case Attribute.MemoryROM:
case Attribute.MemoryPageSize:
case Attribute.Keyboard:
case Attribute.KeyboardDeviceKeys:
case Attribute.KeyboardAppKeys:
case Attribute.KeyboardClickVolumeMax:
case Attribute.DisplayXPixels:
case Attribute.DisplayYPixels:
case Attribute.DisplayXTwips:
case Attribute.DisplayYTwips:
case Attribute.DisplayColors:
case Attribute.DisplayContrastMax:
case Attribute.PenX:
case Attribute.PenY:
case Attribute.PenClickVolumeMax:
case Attribute.MouseX:
case Attribute.MouseY:
case Attribute.MouseButtons:
case Attribute.LEDs:
case Attribute.DisplayBrightnessMax:
case Attribute.KeyboardBacklightState:
case Attribute.AccessoryPower:
case Attribute.NumHalAttributes:
case Attribute.Language:
sb.Append($"{numberExpression.number}");
break;
case Attribute.PowerBackup:
case Attribute.KeyboardClick:
case Attribute.Backlight:
case Attribute.Pen:
case Attribute.PenDisplayOn:
case Attribute.PenClick:
case Attribute.Mouse:
case Attribute.CaseSwitch:
case Attribute.IntegratedPhone:
case Attribute.RemoteInstall:
sb.Append(numberExpression.number == 0 ? "false" : "true");
break;
default:
sb.Append($"0x{numberExpression.number:X8}");
break;
}
attribute = null;
return numberExpression;
default:
throw new ArgumentOutOfRangeException();
}
return null;
}
}

View File

@@ -295,20 +295,24 @@ public sealed partial class Symbian
// description.AppendFormat("{0} = {1}", kvp.Key, kvp.Value).AppendLine();
// Set instance values
_files = new List<DecodedFileRecord>();
_files = new List<DecodedFileRecord>();
_conditions = new List<string>();
uint currentFile = 0;
offset = sh.files_ptr;
var conditionLevel = 0;
do
{
Parse(br, ref offset, ref currentFile, sh.files, languages);
Parse(br, ref offset, ref currentFile, sh.files, languages, ref conditionLevel);
} while(currentFile < sh.files);
description.AppendLine();
// Files appear on .sis in the reverse order they should be processed
_files.Reverse();
// Conditions do as well
_conditions.Reverse();
if(_files.Any(t => t.language is null))
{
@@ -329,6 +333,13 @@ public sealed partial class Symbian
description.AppendLine();
}
if(_conditions.Count > 0)
{
description.AppendLine("Conditions:");
foreach(string condition in _conditions)
description.AppendLine(condition);
}
information = description.ToString();
}

View File

@@ -128,12 +128,13 @@ public sealed partial class Symbian
_files = new List<DecodedFileRecord>();
uint currentFile = 0;
uint offset = sh.files_ptr;
uint currentFile = 0;
uint offset = sh.files_ptr;
var conditionLevel = 0;
do
{
Parse(br, ref offset, ref currentFile, sh.files, languages);
Parse(br, ref offset, ref currentFile, sh.files, languages, ref conditionLevel);
} while(currentFile < sh.files);
// Files appear on .sis in the reverse order they should be processed

View File

@@ -32,22 +32,31 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Aaru.Console;
using Marshal = Aaru.Helpers.Marshal;
namespace Aaru.Archives;
[SuppressMessage("ReSharper", "UnusedMember.Local")]
public sealed partial class Symbian
{
void Parse(BinaryReader br, ref uint offset, ref uint currentFile, uint maxFiles, List<string> languages)
void Parse(BinaryReader br, ref uint offset, ref uint currentFile, uint maxFiles, List<string> languages,
ref int conditionLevel)
{
currentFile++;
if(currentFile > maxFiles)
return;
var tabulationChars = new char[conditionLevel];
for(var i = 0; i < conditionLevel; i++)
tabulationChars[i] = '\t';
string tabulation = new(tabulationChars);
AaruConsole.DebugWriteLine(MODULE_NAME, "Seeking to {0} for parsing of file {1} of {2}", offset, currentFile,
maxFiles);
@@ -58,8 +67,11 @@ public sealed partial class Symbian
br.BaseStream.Seek(-sizeof(FileRecordType), SeekOrigin.Current);
byte[] buffer;
byte[] buffer;
ConditionalRecord conditionalRecord;
StringBuilder conditionSb;
Attribute? nullAttribute;
switch(recordType)
{
case FileRecordType.SimpleFile:
@@ -109,6 +121,129 @@ public sealed partial class Symbian
_files.Add(decodedFileRecord);
if(conditionLevel > 0)
{
bool wait, close;
switch(decodedFileRecord.type)
{
case FileType.FileText:
switch((FileDetails)((uint)decodedFileRecord.details & 0xFF))
{
case FileDetails.TextContinue:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecord.sourceName}\", BUTTONS_CONTINUE, ACTION_CONTINUE)");
break;
case FileDetails.TextSkip:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecord.sourceName}\", BUTTONS_YES_NO, ACTION_SKIP)");
break;
case FileDetails.TextAbort:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecord.sourceName}\", BUTTONS_YES_NO, ACTION_ABORT)");
break;
case FileDetails.TextExit:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecord.sourceName}\", BUTTONS_YES_NO, ACTION_EXIT)");
break;
}
break;
case FileType.FileRun:
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
wait = (decodedFileRecord.details & FileDetails.RunWait) != 0;
close = (decodedFileRecord.details & FileDetails.RunsEnd) != 0;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
switch((FileDetails)((uint)decodedFileRecord.details & 0xFF))
{
case FileDetails.RunInstall:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL, 0)");
}
break;
case FileDetails.RunRemove:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_REMOVE, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_REMOVE, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_REMOVE, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_REMOVE, 0)");
}
break;
case FileDetails.RunBoth:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL | ON_REMOVE, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL | ON_REMOVE, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL | ON_REMOVE, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecord.sourceName}\", ON_INSTALL | ON_REMOVE, 0)");
}
break;
}
break;
case FileType.FileMime:
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
wait = (decodedFileRecord.details & FileDetails.RunWait) != 0;
close = (decodedFileRecord.details & FileDetails.RunsEnd) != 0;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
if(wait && close)
_conditions.Add(tabulation + $"Open(\"{decodedFileRecord.sourceName}\", WAIT | CLOSE)");
else if(close)
_conditions.Add(tabulation + $"Open(\"{decodedFileRecord.sourceName}\", CLOSE)");
else if(wait)
_conditions.Add(tabulation + $"Open(\"{decodedFileRecord.sourceName}\", WAIT)");
else
_conditions.Add(tabulation + $"Open(\"{decodedFileRecord.sourceName}\", 0)");
break;
}
}
break;
case FileRecordType.MultipleLanguageFiles:
MultipleFileRecord multipleFileRecord = new();
@@ -148,7 +283,7 @@ public sealed partial class Symbian
if(multipleFileRecord.record.destinationNameLen > 0)
{
br.BaseStream.Seek(multipleFileRecord.record.destinationNamePtr, SeekOrigin.Begin);
buffer = br.ReadBytes((int)multipleFileRecord.record.destinationNameLen);
buffer = br.ReadBytes((int)multipleFileRecord.record.destinationNameLen);
destinationName = _encoding.GetString(buffer);
}
else
@@ -180,17 +315,202 @@ public sealed partial class Symbian
_files.AddRange(decodedFileRecords);
if(conditionLevel > 0)
{
bool wait, close;
switch(decodedFileRecords[0].type)
{
case FileType.FileText:
switch((FileDetails)((uint)decodedFileRecords[0].details & 0xFF))
{
case FileDetails.TextContinue:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecords[0].sourceName}\", BUTTONS_CONTINUE, ACTION_CONTINUE)");
break;
case FileDetails.TextSkip:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecords[0].sourceName}\", BUTTONS_YES_NO, ACTION_SKIP)");
break;
case FileDetails.TextAbort:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecords[0].sourceName}\", BUTTONS_YES_NO, ACTION_ABORT)");
break;
case FileDetails.TextExit:
_conditions.Add(tabulation +
$"ShowText(\"{decodedFileRecords[0].sourceName}\", BUTTONS_YES_NO, ACTION_EXIT)");
break;
}
break;
case FileType.FileRun:
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
wait = (decodedFileRecords[0].details & FileDetails.RunWait) != 0;
close = (decodedFileRecords[0].details & FileDetails.RunsEnd) != 0;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
switch((FileDetails)((uint)decodedFileRecords[0].details & 0xFF))
{
case FileDetails.RunInstall:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL, 0)");
}
break;
case FileDetails.RunRemove:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_REMOVE, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_REMOVE, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_REMOVE, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_REMOVE, 0)");
}
break;
case FileDetails.RunBoth:
if(wait && close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL | ON_REMOVE, WAIT | CLOSE)");
}
else if(close)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL | ON_REMOVE, CLOSE)");
}
else if(wait)
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL | ON_REMOVE, WAIT)");
}
else
{
_conditions.Add(tabulation +
$"Run(\"{decodedFileRecords[0].sourceName}\", ON_INSTALL | ON_REMOVE, 0)");
}
break;
}
break;
case FileType.FileMime:
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
wait = (decodedFileRecords[0].details & FileDetails.RunWait) != 0;
close = (decodedFileRecords[0].details & FileDetails.RunsEnd) != 0;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
if(wait && close)
{
_conditions.Add(tabulation +
$"Open(\"{decodedFileRecords[0].sourceName}\", WAIT | CLOSE)");
}
else if(close)
_conditions.Add(tabulation + $"Open(\"{decodedFileRecords[0].sourceName}\", CLOSE)");
else if(wait)
_conditions.Add(tabulation + $"Open(\"{decodedFileRecords[0].sourceName}\", WAIT)");
else
_conditions.Add(tabulation + $"Open(\"{decodedFileRecords[0].sourceName}\", 0)");
break;
}
}
break;
case FileRecordType.Options:
throw new NotImplementedException();
case FileRecordType.If:
throw new NotImplementedException();
conditionLevel--;
tabulationChars = new char[conditionLevel];
for(var i = 0; i < conditionLevel; i++)
tabulationChars[i] = '\t';
tabulation = new string(tabulationChars);
conditionalRecord = new ConditionalRecord
{
recordType = (FileRecordType)br.ReadUInt32(),
length = br.ReadUInt32()
};
offset = (uint)(br.BaseStream.Position + conditionalRecord.length);
conditionSb = new StringBuilder();
nullAttribute = null;
conditionSb.Append(tabulation + "if(");
ParseConditionalExpression(br, offset, conditionSb, ref nullAttribute);
conditionSb.Append(")");
_conditions.Add(conditionSb.ToString());
break;
case FileRecordType.ElseIf:
throw new NotImplementedException();
conditionLevel--;
tabulationChars = new char[conditionLevel];
for(var i = 0; i < conditionLevel; i++)
tabulationChars[i] = '\t';
tabulation = new string(tabulationChars);
conditionalRecord = new ConditionalRecord
{
recordType = (FileRecordType)br.ReadUInt32(),
length = br.ReadUInt32()
};
offset = (uint)(br.BaseStream.Position + conditionalRecord.length);
conditionSb = new StringBuilder();
nullAttribute = null;
conditionSb.Append(tabulation + "else if(");
ParseConditionalExpression(br, offset, conditionSb, ref nullAttribute);
conditionSb.Append(")");
_conditions.Add(conditionSb.ToString());
break;
case FileRecordType.Else:
throw new NotImplementedException();
tabulationChars = new char[conditionLevel - 1];
for(var i = 0; i < conditionLevel - 1; i++)
tabulationChars[i] = '\t';
tabulation = new string(tabulationChars);
_conditions.Add(tabulation + "else");
offset = (uint)(br.BaseStream.Position + Marshal.SizeOf<ConditionalEndRecord>());
break;
case FileRecordType.EndIf:
throw new NotImplementedException();
conditionLevel++;
_conditions.Add(tabulation + "endif()" + Environment.NewLine);
offset = (uint)(br.BaseStream.Position + Marshal.SizeOf<ConditionalEndRecord>());
break;
default:
throw new ArgumentOutOfRangeException();
}

View File

@@ -44,6 +44,7 @@ namespace Aaru.Archives;
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "InheritdocConsiderUsage")]
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
[SuppressMessage("ReSharper", "ClassCanBeSealed.Local")]
public sealed partial class Symbian
{
#region Nested type: AttributeConditionalExpression
@@ -420,8 +421,9 @@ public sealed partial class Symbian
/// </summary>
class StringConditionalExpression : ConditionalExpression
{
public uint length;
public uint pointer;
public uint length;
public uint pointer;
public string @string;
}
#endregion

View File

@@ -43,6 +43,7 @@ public sealed partial class Symbian : IArchive
{
const string MODULE_NAME = "Symbian Installation File Plugin";
bool _compressed;
List<string> _conditions;
Encoding _encoding;
ArchiveSupportedFeature _features;
List<DecodedFileRecord> _files;