Files
Matt Nadareski 8f49e190d8 Fix everything
2026-03-24 19:17:25 -04:00

1791 lines
65 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.Data.Models.WiseInstaller;
using SabreTools.Data.Models.WiseInstaller.Actions;
using SabreTools.IO.Extensions;
using SabreTools.Numerics.Extensions;
using SabreTools.Text.Extensions;
#pragma warning disable CA1866 // Use char overload
#pragma warning disable IDE0017 // Simplify object initialization
#pragma warning disable IDE0060 // Remove unused parameter
namespace SabreTools.Serialization.Readers
{
public class WiseScript : BaseBinaryReader<ScriptFile>
{
/// <inheritdoc/>
public override ScriptFile? Deserialize(Stream? data)
{
// If the data is invalid
if (data is null || !data.CanRead)
return null;
try
{
// Cache the current offset
long initialOffset = data.Position;
var script = new ScriptFile();
#region Header
var header = ParseScriptHeader(data);
script.Header = header;
#endregion
#region State Machine
var states = ParseStateMachine(data, header);
if (states is null)
return null;
script.States = states;
#endregion
return script;
}
catch
{
// Ignore the actual error
return null;
}
}
/// <summary>
/// Parse a Stream into a ScriptHeader
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ScriptHeader on success, null on error</returns>
private static ScriptHeader ParseScriptHeader(Stream data)
{
// Cache the current position in case of a trimmed header
long current = data.Position;
var header = new ScriptHeader();
// Attempt to read strings at 0x12 (Short)
data.SeekIfPossible(current + 0x12, SeekOrigin.Begin);
string? ftpUrl = data.ReadNullTerminatedAnsiString();
string? logPath = data.ReadNullTerminatedAnsiString();
string? messageFont = data.ReadNullTerminatedAnsiString();
data.SeekIfPossible(current, SeekOrigin.Begin);
// If the strings are valid
if (ftpUrl is not null && (ftpUrl.Length == 0 || ftpUrl.Split('.').Length > 2)
&& logPath is not null && (logPath.Length == 0 || logPath.StartsWith("%"))
&& messageFont is not null && (messageFont.Length == 0 || !IsTypicalControlCode(messageFont, strict: true))
&& !(ftpUrl.Length == 0 && logPath.Length == 0 && messageFont.Length == 0))
{
// TODO: Figure out if this maps to existing fields
header.Flags = data.ReadByteValue();
header.UnknownU16_1 = data.ReadUInt16LittleEndian();
header.UnknownU16_2 = data.ReadUInt16LittleEndian();
header.DateTime = data.ReadUInt32LittleEndian();
header.VariableLengthData = data.ReadBytes(9);
goto ReadStrings;
}
// Attempt to read strings at 0x26 (Middle)
data.SeekIfPossible(current + 0x26, SeekOrigin.Begin);
ftpUrl = data.ReadNullTerminatedAnsiString();
logPath = data.ReadNullTerminatedAnsiString();
messageFont = data.ReadNullTerminatedAnsiString();
data.SeekIfPossible(current, SeekOrigin.Begin);
// If the strings are valid
if (ftpUrl is not null && (ftpUrl.Length == 0 || ftpUrl.Split('.').Length > 2)
&& logPath is not null && (logPath.Length == 0 || logPath.StartsWith("%"))
&& messageFont is not null && (messageFont.Length == 0 || !IsTypicalControlCode(messageFont, strict: true))
&& !(ftpUrl.Length == 0 && logPath.Length == 0 && messageFont.Length == 0))
{
header.Flags = data.ReadByteValue();
header.UnknownU16_1 = data.ReadUInt16LittleEndian();
header.UnknownU16_2 = data.ReadUInt16LittleEndian();
header.SomeOffset1 = data.ReadUInt32LittleEndian();
header.SomeOffset2 = data.ReadUInt32LittleEndian();
header.UnknownBytes_2 = data.ReadBytes(4);
header.DateTime = data.ReadUInt32LittleEndian();
header.VariableLengthData = data.ReadBytes(17);
goto ReadStrings;
}
// Attempt to read strings at 0x34 (Long)
data.SeekIfPossible(current + 0x34, SeekOrigin.Begin);
ftpUrl = data.ReadNullTerminatedAnsiString();
logPath = data.ReadNullTerminatedAnsiString();
messageFont = data.ReadNullTerminatedAnsiString();
data.SeekIfPossible(current, SeekOrigin.Begin);
// If the strings are valid
if (ftpUrl is not null && (ftpUrl.Length == 0 || ftpUrl.Split('.').Length > 2)
&& logPath is not null && (logPath.Length == 0 || logPath.StartsWith("%"))
&& messageFont is not null && (messageFont.Length == 0 || !IsTypicalControlCode(messageFont, strict: true))
&& !(ftpUrl.Length == 0 && logPath.Length == 0 && messageFont.Length == 0))
{
header.Flags = data.ReadByteValue();
header.UnknownU16_1 = data.ReadUInt16LittleEndian();
header.UnknownU16_2 = data.ReadUInt16LittleEndian();
header.SomeOffset1 = data.ReadUInt32LittleEndian();
header.SomeOffset2 = data.ReadUInt32LittleEndian();
header.UnknownBytes_2 = data.ReadBytes(4);
header.DateTime = data.ReadUInt32LittleEndian();
header.VariableLengthData = data.ReadBytes(31);
goto ReadStrings;
}
// Otherwise, assume a standard header (Normal)
header.Flags = data.ReadByteValue();
header.UnknownU16_1 = data.ReadUInt16LittleEndian();
header.UnknownU16_2 = data.ReadUInt16LittleEndian();
header.SomeOffset1 = data.ReadUInt32LittleEndian();
header.SomeOffset2 = data.ReadUInt32LittleEndian();
header.UnknownBytes_2 = data.ReadBytes(4);
header.DateTime = data.ReadUInt32LittleEndian();
header.VariableLengthData = data.ReadBytes(22);
ReadStrings:
header.FTPURL = data.ReadNullTerminatedAnsiString();
header.LogPathname = data.ReadNullTerminatedAnsiString();
header.MessageFont = data.ReadNullTerminatedAnsiString();
header.FontSize = data.ReadUInt32LittleEndian();
header.Unknown_2 = data.ReadBytes(2);
header.LanguageCount = data.ReadByteValue();
List<string> headerStrings = [];
while (true)
{
string? str = data.ReadNullTerminatedAnsiString();
if (str is null)
break;
// Try to handle invalid string lengths
if (str.Length > 0 && IsTypicalControlCode(str, strict: false))
{
data.SeekIfPossible(-str.Length - 1, SeekOrigin.Current);
break;
}
// Try to handle InstallFile calls
long original = data.Position;
if (str.Length == 0)
{
data.SeekIfPossible(-1, SeekOrigin.Current);
// Try to read the next block as an install file call
var maybeInstall = ParseInstallFile(data, header.LanguageCount);
if (maybeInstall is not null
&& (maybeInstall.DeflateEnd - maybeInstall.DeflateStart) < data.Length
&& (maybeInstall.DeflateEnd - maybeInstall.DeflateStart) < maybeInstall.InflatedSize)
{
data.SeekIfPossible(original - 1, SeekOrigin.Begin);
break;
}
// Otherwise, seek back to reading
data.SeekIfPossible(original, SeekOrigin.Begin);
}
headerStrings.Add(str);
}
header.HeaderStrings = [.. headerStrings];
return header;
}
/// <summary>
/// Parse a Stream into a state machine
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="header">Parsed script header for information</param>
/// <param name="old">Indicates an old install script</param>
/// <returns>Filled state machine on success, null on error</returns>
private static MachineState[]? ParseStateMachine(Stream data, ScriptHeader header)
{
// Extract required information
byte languageCount = header.LanguageCount;
bool shortDllCall = header.VariableLengthData?.Length != 22
&& header.SomeOffset1 == 0x00000000
&& header.Flags != 0x0008
&& header.Flags != 0x0014;
// Initialize important loop information
int op0x18skip = -1;
bool? registryDll = null;
bool switched = false;
// Store the start of the state machine
long machineStart = data.Position;
// Store all states in order
List<MachineState> states = [];
while (data.Position < data.Length)
{
var op = (OperationCode)data.ReadByteValue();
MachineStateData? stateData = op switch
{
OperationCode.InstallFile => ParseInstallFile(data, languageCount),
OperationCode.Invalid0x01 => ParseInvalidOperation(data),
OperationCode.NoOp => ParseNoOp(data),
OperationCode.DisplayMessage => ParseDisplayMessage(data, languageCount),
OperationCode.UserDefinedActionStep => ParseUserDefinedActionStep(data, languageCount),
OperationCode.EditIniFile => ParseEditIniFile(data),
OperationCode.DisplayBillboard => ParseDisplayBillboard(data, languageCount),
OperationCode.ExecuteProgram => ParseExecuteProgram(data),
OperationCode.EndBlock => ParseEndBlockStatement(data),
OperationCode.CallDllFunction => ParseCallDllFunction(data, languageCount, shortDllCall),
OperationCode.EditRegistry => ParseEditRegistry(data, ref registryDll),
OperationCode.DeleteFile => ParseDeleteFile(data),
OperationCode.IfWhileStatement => ParseIfWhileStatement(data),
OperationCode.ElseStatement => ParseElseStatement(data),
OperationCode.Invalid0x0E => ParseInvalidOperation(data),
OperationCode.StartUserDefinedAction => ParseStartUserDefinedAction(data),
OperationCode.EndUserDefinedAction => ParseEndUserDefinedAction(data),
OperationCode.CreateDirectory => ParseCreateDirectory(data),
OperationCode.CopyLocalFile => ParseCopyLocalFile(data, languageCount),
OperationCode.Invalid0x13 => ParseInvalidOperation(data),
OperationCode.CustomDialogSet => ParseCustomDialogSet(data),
OperationCode.GetSystemInformation => ParseGetSystemInformation(data),
OperationCode.GetTemporaryFilename => ParseGetTemporaryFilename(data),
OperationCode.PlayMultimediaFile => ParsePlayMultimediaFile(data),
OperationCode.NewEvent => ParseNewEvent(data, languageCount, shortDllCall, ref op0x18skip),
OperationCode.InstallODBCDriver => ParseUnknown0x19(data),
OperationCode.ConfigODBCDataSource => ParseConfigODBCDataSource(data),
OperationCode.IncludeScript => ParseIncludeScript(data),
OperationCode.AddTextToInstallLog => ParseAddTextToInstallLog(data),
OperationCode.RenameFileDirectory => ParseRenameFileDirectory(data),
OperationCode.OpenCloseInstallLog => ParseOpenCloseInstallLog(data),
OperationCode.Invalid0x1F => ParseInvalidOperation(data),
OperationCode.Invalid0x20 => ParseInvalidOperation(data),
OperationCode.Invalid0x21 => ParseInvalidOperation(data),
OperationCode.Invalid0x22 => ParseInvalidOperation(data),
OperationCode.ElseIfStatement => ParseElseIfStatement(data),
OperationCode.Unknown0x24 => ParseUnknown0x24(data),
OperationCode.Unknown0x25 => ParseUnknown0x25(data),
// Handled separately below
_ => null,
};
// If an error is detected, try parsing with flipped short DLL call values
if (stateData is null)
{
// If there has already been one switch, don't try again
if (switched)
throw new IndexOutOfRangeException(nameof(op));
// Debug statement
Console.WriteLine($"Opcode {op} resulted in null data, trying with alternate values");
// Reset the state
switched = true;
shortDllCall = !shortDllCall;
states.Clear();
// Seek to the start of the machine and try again
data.SeekIfPossible(machineStart, SeekOrigin.Begin);
continue;
}
var state = new MachineState
{
Op = op,
Data = stateData,
};
states.Add(state);
}
return [.. states];
}
#region State Actions
/// <summary>
/// Parse a Stream into a InstallFile
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled InstallFile on success, null on error</returns>
private static InstallFile ParseInstallFile(Stream data, int languageCount)
{
var header = new InstallFile();
header.Flags = data.ReadUInt16LittleEndian();
header.DeflateStart = data.ReadUInt32LittleEndian();
header.DeflateEnd = data.ReadUInt32LittleEndian();
header.Date = data.ReadUInt16LittleEndian();
header.Time = data.ReadUInt16LittleEndian();
header.InflatedSize = data.ReadUInt32LittleEndian();
header.Operand_7 = data.ReadBytes(20);
header.Crc32 = data.ReadUInt32LittleEndian();
header.DestinationPathname = data.ReadNullTerminatedAnsiString();
header.Description = new string[languageCount];
for (int i = 0; i < header.Description.Length; i++)
{
header.Description[i] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
}
header.Source = data.ReadNullTerminatedAnsiString();
return header;
}
/// <summary>
/// Parse a Stream into a NoOp
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled NoOp on success, null on error</returns>
private static NoOp ParseNoOp(Stream data)
{
return new NoOp();
}
/// <summary>
/// Parse a Stream into a DisplayMessage
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <returns>Filled DisplayMessage on success, null on error</returns>
private static DisplayMessage ParseDisplayMessage(Stream data, int languageCount)
{
var obj = new DisplayMessage();
obj.Flags = data.ReadByteValue();
obj.TitleText = new string[languageCount * 2];
for (int i = 0; i < obj.TitleText.Length; i++)
{
obj.TitleText[i] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
}
return obj;
}
/// <summary>
/// Parse a Stream into a UserDefinedActionStep
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <returns>Filled UserDefinedActionStep on success, null on error</returns>
private static UserDefinedActionStep ParseUserDefinedActionStep(Stream data, int languageCount)
{
var obj = new UserDefinedActionStep();
obj.Flags = data.ReadByteValue();
obj.ScriptLines = new string[languageCount];
for (int i = 0; i < obj.ScriptLines.Length; i++)
{
obj.ScriptLines[i] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
}
return obj;
}
/// <summary>
/// Parse a Stream into an EditIniFile
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled EditIniFile on success, null on error</returns>
private static EditIniFile ParseEditIniFile(Stream data)
{
var obj = new EditIniFile();
obj.Pathname = data.ReadNullTerminatedAnsiString();
obj.Section = data.ReadNullTerminatedAnsiString();
obj.Values = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a DisplayBillboard
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <returns>Filled DisplayBillboard on success, null on error</returns>
private static DisplayBillboard ParseDisplayBillboard(Stream data, int languageCount)
{
var obj = new DisplayBillboard();
obj.Flags = data.ReadUInt16LittleEndian();
obj.Operand_2 = data.ReadUInt16LittleEndian();
obj.Operand_3 = data.ReadUInt16LittleEndian();
obj.DeflateInfo = new DeflateEntry[languageCount];
for (int i = 0; i < obj.DeflateInfo.Length; i++)
{
obj.DeflateInfo[i] = ParseDeflateEntry(data);
}
// Check the terminator byte is 0x00
obj.Terminator = data.ReadByteValue();
if (obj.Terminator != 0x00)
{
obj.Terminator = 0x00;
data.SeekIfPossible(-1, SeekOrigin.Current);
}
return obj;
}
/// <summary>
/// Parse a Stream into a ExecuteProgram
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ExecuteProgram on success, null on error</returns>
private static ExecuteProgram ParseExecuteProgram(Stream data)
{
var obj = new ExecuteProgram();
obj.Flags = data.ReadByteValue();
obj.Pathname = data.ReadNullTerminatedAnsiString();
obj.CommandLine = data.ReadNullTerminatedAnsiString();
obj.DefaultDirectory = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a EndBlockStatement
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled EndBlockStatement on success, null on error</returns>
private static EndBlockStatement ParseEndBlockStatement(Stream data)
{
var obj = new EndBlockStatement();
obj.Operand_1 = data.ReadByteValue();
return obj;
}
/// <summary>
/// Parse a Stream into a CallDllFunction
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <param name="shortDllCall">Indicates a short DLL call</param>
/// <returns>Filled CallDllFunction on success, null on error</returns>
private static CallDllFunction ParseCallDllFunction(Stream data, int languageCount, bool shortDllCall)
{
var obj = new CallDllFunction();
obj.Flags = data.ReadByteValue();
obj.DllPath = data.ReadNullTerminatedAnsiString();
obj.FunctionName = data.ReadNullTerminatedAnsiString();
if (!shortDllCall)
{
obj.Operand_4 = data.ReadNullTerminatedAnsiString();
obj.ReturnVariable = data.ReadNullTerminatedAnsiString();
}
obj.Entries = new FunctionData[languageCount];
for (int i = 0; i < obj.Entries.Length; i++)
{
// Switch based on the function
string entryString = data.ReadNullTerminatedAnsiString() ?? string.Empty;
obj.Entries[i] = obj.FunctionName switch
{
"f0" => ParseAddDirectoryToPath(entryString),
"f1" => ParseAddToAutoexecBat(entryString),
"f2" => ParseAddToConfigSys(entryString),
"f3" => ParseAddToSystemIni(entryString),
"f8" => ParseReadIniValue(entryString),
"f9" => ParseGetRegistryKeyValue(entryString),
"f10" => ParseRegisterFont(entryString),
"f11" => ParseWin32SystemDirectory(entryString),
"f12" => ParseCheckConfiguration(entryString),
"f13" => ParseSearchForFile(entryString),
"f15" => ParseReadWriteBinaryFile(entryString),
"f16" => ParseSetVariable(entryString),
"f17" => ParseGetEnvironmentVariable(entryString),
"f19" => ParseCheckIfFileDirExists(entryString),
"f20" => ParseSetFileAttributes(entryString),
"f21" => ParseSetFilesBuffers(entryString),
"f22" => ParseFindFileInPath(entryString),
"f23" => ParseCheckDiskSpace(entryString),
"f25" => ParseInsertLineIntoTextFile(entryString),
"f27" => ParseParseString(entryString),
"f28" => ParseExitInstallation(entryString),
"f29" => ParseSelfRegisterOCXsDLLs(entryString),
"f30" => ParseInstallDirectXComponents(entryString),
"f31" => ParseWizardBlockLoop(entryString),
"f33" => ParseReadUpdateTextFile(entryString),
"f34" => ParsePostToHttpServer(entryString),
"f35" => ParsePromptForFilename(entryString),
"f36" => ParseStartStopService(entryString),
"f38" => ParseCheckHttpConnection(entryString),
// External and unrecognized functions
_ => ParseExternalDllCall(entryString),
};
// Log if a truely unknown function is found
if (obj.Entries[i] is ExternalDllCall edc && string.IsNullOrEmpty(obj.DllPath))
Console.WriteLine($"Unrecognized function: {obj.FunctionName} with parts: {string.Join(", ", edc.Args ?? [])}");
}
return obj;
}
/// <summary>
/// Parse a Stream into a EditRegistry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="registryDll">Indicates if the longer value set is used</param>
/// <returns>Filled EditRegistry on success, null on error</returns>
private static EditRegistry ParseEditRegistry(Stream data, ref bool? registryDll)
{
var obj = new EditRegistry();
obj.FlagsAndRoot = data.ReadByteValue();
// If the fsllib32.dll flag is set
if (registryDll == false)
{
obj.DataType = data.ReadByteValue();
obj.Key = data.ReadNullTerminatedAnsiString();
obj.NewValue = data.ReadNullTerminatedAnsiString();
obj.ValueName = data.ReadNullTerminatedAnsiString();
return obj;
}
else if (registryDll == true)
{
obj.DataType = data.ReadByteValue();
obj.UnknownFsllib = data.ReadNullTerminatedAnsiString();
obj.Key = data.ReadNullTerminatedAnsiString();
obj.NewValue = data.ReadNullTerminatedAnsiString();
obj.ValueName = data.ReadNullTerminatedAnsiString();
return obj;
}
// Check for an empty registry call
uint possiblyEmpty = data.PeekUInt32LittleEndian();
if (possiblyEmpty == 0x00000000)
{
obj.DataType = data.ReadByteValue();
obj.Key = data.ReadNullTerminatedAnsiString();
obj.NewValue = data.ReadNullTerminatedAnsiString();
obj.ValueName = data.ReadNullTerminatedAnsiString();
registryDll = false;
return obj;
}
// Assume use until otherwise determined
registryDll = true;
obj.DataType = data.ReadByteValue();
obj.UnknownFsllib = data.ReadNullTerminatedAnsiString();
obj.Key = data.ReadNullTerminatedAnsiString();
obj.NewValue = data.ReadNullTerminatedAnsiString();
obj.ValueName = data.ReadNullTerminatedAnsiString();
// If the delete pattern is found
if (obj.UnknownFsllib is not null && obj.UnknownFsllib.Length > 0
&& obj.Key is not null && obj.Key.Length == 0
&& obj.NewValue is not null && obj.NewValue.Length == 0)
{
data.SeekIfPossible(-(obj.ValueName?.Length ?? 0) - 1, SeekOrigin.Current);
obj.ValueName = obj.NewValue;
obj.NewValue = obj.Key;
obj.Key = obj.UnknownFsllib;
obj.UnknownFsllib = null;
registryDll = false;
}
// If the last value is a control
else if (obj.ValueName is not null && IsTypicalControlCode(obj.ValueName, strict: true))
{
data.SeekIfPossible(-obj.ValueName.Length - 1, SeekOrigin.Current);
obj.ValueName = obj.NewValue;
obj.NewValue = obj.Key;
obj.Key = obj.UnknownFsllib;
obj.UnknownFsllib = null;
registryDll = false;
}
return obj;
}
/// <summary>
/// Parse a Stream into a DeleteFile
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled DeleteFile on success, null on error</returns>
private static DeleteFile ParseDeleteFile(Stream data)
{
var obj = new DeleteFile();
obj.Flags = data.ReadByteValue();
obj.Pathname = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a IfWhileStatement
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled IfWhileStatement on success, null on error</returns>
private static IfWhileStatement ParseIfWhileStatement(Stream data)
{
var obj = new IfWhileStatement();
obj.Flags = data.ReadByteValue();
obj.Variable = data.ReadNullTerminatedAnsiString();
obj.Value = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into an ElseStatement
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ElseStatement on success, null on error</returns>
private static ElseStatement ParseElseStatement(Stream data)
{
return new ElseStatement();
}
/// <summary>
/// Parse a Stream into an ElseStatement
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled StartUserDefinedAction on success, null on error</returns>
private static StartUserDefinedAction ParseStartUserDefinedAction(Stream data)
{
return new StartUserDefinedAction();
}
/// <summary>
/// Parse a Stream into an EndUserDefinedAction
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled EndUserDefinedAction on success, null on error</returns>
private static EndUserDefinedAction ParseEndUserDefinedAction(Stream data)
{
return new EndUserDefinedAction();
}
/// <summary>
/// Parse a Stream into a CreateDirectory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled CreateDirectory on success, null on error</returns>
private static CreateDirectory ParseCreateDirectory(Stream data)
{
var obj = new CreateDirectory();
obj.Pathname = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a CopyLocalFile
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <returns>Filled CopyLocalFile on success, null on error</returns>
private static CopyLocalFile ParseCopyLocalFile(Stream data, int languageCount)
{
var obj = new CopyLocalFile();
obj.Flags = data.ReadUInt16LittleEndian();
obj.Padding = data.ReadBytes(40);
obj.Destination = data.ReadNullTerminatedAnsiString();
obj.Description = new string[languageCount + 1];
for (int i = 0; i < obj.Description.Length; i++)
{
obj.Description[i] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
}
obj.Source = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a CustomDialogSet
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled CustomDialogSet on success, null on error</returns>
private static CustomDialogSet ParseCustomDialogSet(Stream data)
{
var obj = new CustomDialogSet();
obj.DeflateStart = data.ReadUInt32LittleEndian();
obj.DeflateEnd = data.ReadUInt32LittleEndian();
obj.InflatedSize = data.ReadUInt32LittleEndian();
obj.DisplayVariable = data.ReadNullTerminatedAnsiString();
obj.Name = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a GetSystemInformation
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled GetSystemInformation on success, null on error</returns>
private static GetSystemInformation ParseGetSystemInformation(Stream data)
{
var obj = new GetSystemInformation();
obj.Flags = data.ReadByteValue();
obj.Variable = data.ReadNullTerminatedAnsiString();
obj.Pathname = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a GetTemporaryFilename
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled GetTemporaryFilename on success, null on error</returns>
private static GetTemporaryFilename ParseGetTemporaryFilename(Stream data)
{
var obj = new GetTemporaryFilename();
obj.Variable = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a PlayMultimediaFile
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled PlayMultimediaFile on success, null on error</returns>
private static PlayMultimediaFile ParsePlayMultimediaFile(Stream data)
{
var obj = new PlayMultimediaFile();
obj.Flags = data.ReadByteValue();
obj.XPosition = data.ReadUInt16LittleEndian();
obj.YPosition = data.ReadUInt16LittleEndian();
obj.Pathname = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a NewEvent
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="languageCount">Language counter from the header</param>
/// <param name="shortDllCall">Indicates a short DLL call</param>
/// <param name="op0x18skip">Current 0x18 skip value</param>
/// <returns>Filled NewEvent on success, null on error</returns>
internal static NewEvent ParseNewEvent(Stream data, int languageCount, bool shortDllCall, ref int op0x18skip)
{
// If the end of the stream has been reached
if (data.Position >= data.Length)
return new NewEvent();
// If the skip amount needs to be determined
if (op0x18skip == -1)
{
long current = data.Position;
byte nextByte = data.ReadByteValue();
data.SeekIfPossible(current, SeekOrigin.Begin);
op0x18skip = nextByte == 0 || nextByte == 0xFF ? 6 : 0;
if (nextByte == 0x09)
{
var possible = ParseCallDllFunction(data, languageCount, shortDllCall);
op0x18skip = (possible.FunctionName is null || possible.FunctionName.Length == 0) ? 6 : 0;
data.SeekIfPossible(current, SeekOrigin.Begin);
}
}
var obj = new NewEvent();
// Skip additional bytes
if (op0x18skip > 0)
obj.Padding = data.ReadBytes(op0x18skip);
return obj;
}
/// <summary>
/// Parse a Stream into a Unknown0x19
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Unknown0x19 on success, null on error</returns>
private static Unknown0x19 ParseUnknown0x19(Stream data)
{
var obj = new Unknown0x19();
obj.Operand_1 = data.ReadByteValue();
obj.Operand_2 = data.ReadNullTerminatedAnsiString();
obj.Operand_3 = data.ReadNullTerminatedAnsiString();
obj.Operand_4 = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a ConfigODBCDataSource
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ConfigODBCDataSource on success, null on error</returns>
private static ConfigODBCDataSource ParseConfigODBCDataSource(Stream data)
{
var obj = new ConfigODBCDataSource();
obj.Flags = data.ReadByteValue();
obj.FileFormat = data.ReadNullTerminatedAnsiString();
obj.ConnectionString = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into an IncludeScript
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled IncludeScript on success, null on error</returns>
private static IncludeScript ParseIncludeScript(Stream data)
{
var obj = new IncludeScript();
obj.Count = 0;
while (data.Position < data.Length && data.ReadByteValue() == 0x1B)
{
obj.Count++;
}
// Rewind if one was found
if (data.Position < data.Length)
data.SeekIfPossible(-1, SeekOrigin.Current);
return obj;
}
/// <summary>
/// Parse a Stream into a AddTextToInstallLog
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled AddTextToInstallLog on success, null on error</returns>
private static AddTextToInstallLog ParseAddTextToInstallLog(Stream data)
{
var obj = new AddTextToInstallLog();
obj.Text = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a RenameFileDirectory
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled RenameFileDirectory on success, null on error</returns>
private static RenameFileDirectory ParseRenameFileDirectory(Stream data)
{
var obj = new RenameFileDirectory();
obj.OldPathname = data.ReadNullTerminatedAnsiString();
obj.NewFileName = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a OpenCloseInstallLog
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled OpenCloseInstallLog on success, null on error</returns>
private static OpenCloseInstallLog ParseOpenCloseInstallLog(Stream data)
{
var obj = new OpenCloseInstallLog();
obj.Flags = data.ReadByteValue();
obj.LogName = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a ElseIfStatement
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ElseIfStatement on success, null on error</returns>
private static ElseIfStatement ParseElseIfStatement(Stream data)
{
var obj = new ElseIfStatement();
obj.Operator = data.ReadByteValue();
obj.Variable = data.ReadNullTerminatedAnsiString();
obj.Value = data.ReadNullTerminatedAnsiString();
return obj;
}
/// <summary>
/// Parse a Stream into a Unknown0x24
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Unknown0x24 on success, null on error</returns>
private static Unknown0x24 ParseUnknown0x24(Stream data)
{
return new Unknown0x24();
}
/// <summary>
/// Parse a Stream into a Unknown0x25
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled Unknown0x25 on success, null on error</returns>
private static Unknown0x25 ParseUnknown0x25(Stream data)
{
return new Unknown0x25();
}
/// <summary>
/// Parse a Stream into a InvalidOperation
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled InvalidOperation on success, null on error</returns>
/// <remarks>
/// This represents a placeholder. The operations that result in this
/// type being parsed should never happen in the state machine.
/// </remarks>
private static InvalidOperation ParseInvalidOperation(Stream data)
{
return new InvalidOperation();
}
#endregion
#region Function Actions
/// <summary>
/// Parse a string into a AddDirectoryToPath
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled AddDirectoryToPath on success, null on error</returns>
private static AddDirectoryToPath ParseAddDirectoryToPath(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new AddDirectoryToPath();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Directory = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a AddToAutoexecBat
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled AddToAutoexecBat on success, null on error</returns>
private static AddToAutoexecBat ParseAddToAutoexecBat(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new AddToAutoexecBat();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.FileToEdit = parts[1];
if (parts.Length > 2)
obj.TextToInsert = parts[2];
if (parts.Length > 3)
obj.SearchForText = parts[3];
if (parts.Length > 4)
obj.CommentText = parts[4];
if (parts.Length > 5 && int.TryParse(parts[5], out int lineNumber))
obj.LineNumber = lineNumber;
return obj;
}
/// <summary>
/// Parse a string into a AddToConfigSys
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled AddToConfigSys on success, null on error</returns>
private static AddToConfigSys ParseAddToConfigSys(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new AddToConfigSys();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.FileToEdit = parts[1];
if (parts.Length > 2)
obj.TextToInsert = parts[2];
if (parts.Length > 3)
obj.SearchForText = parts[3];
if (parts.Length > 4)
obj.CommentText = parts[4];
if (parts.Length > 5 && int.TryParse(parts[5], out int lineNumber))
obj.LineNumber = lineNumber;
return obj;
}
/// <summary>
/// Parse a string into a AddToSystemIni
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled AddToSystemIni on success, null on error</returns>
private static AddToSystemIni ParseAddToSystemIni(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new AddToSystemIni();
if (parts.Length > 0)
obj.DeviceName = parts[0];
return obj;
}
/// <summary>
/// Parse a string into a ReadIniValue
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ReadIniValue on success, null on error</returns>
private static ReadIniValue ParseReadIniValue(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new ReadIniValue();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.Pathname = parts[2];
if (parts.Length > 3)
obj.Section = parts[3];
if (parts.Length > 4)
obj.Item = parts[4];
if (parts.Length > 5)
obj.DefaultValue = parts[5];
return obj;
}
/// <summary>
/// Parse a string into a GetRegistryKeyValue
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled GetRegistryKeyValue on success, null on error</returns>
private static GetRegistryKeyValue ParseGetRegistryKeyValue(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new GetRegistryKeyValue();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.Key = parts[2];
if (parts.Length > 3)
obj.Default = parts[3];
if (parts.Length > 4)
obj.ValueName = parts[4];
if (parts.Length > 5)
obj.Root = parts[5];
return obj;
}
/// <summary>
/// Parse a string into a RegisterFont
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled RegisterFont on success, null on error</returns>
private static RegisterFont ParseRegisterFont(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new RegisterFont();
if (parts.Length > 0)
obj.FontFileName = parts[0];
if (parts.Length > 1)
obj.FontName = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a Win32SystemDirectory
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled Win32SystemDirectory on success, null on error</returns>
private static Win32SystemDirectory ParseWin32SystemDirectory(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new Win32SystemDirectory();
if (parts.Length > 0)
obj.VariableName = parts[0];
return obj;
}
/// <summary>
/// Parse a string into a CheckConfiguration
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled CheckConfiguration on success, null on error</returns>
private static CheckConfiguration ParseCheckConfiguration(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new CheckConfiguration();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Message = parts[1];
if (parts.Length > 2)
obj.Title = parts[2];
return obj;
}
/// <summary>
/// Parse a string into a SearchForFile
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled SearchForFile on success, null on error</returns>
private static SearchForFile ParseSearchForFile(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new SearchForFile();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.FileName = parts[2];
if (parts.Length > 3)
obj.FileName = parts[3];
if (parts.Length > 4)
obj.MessageText = parts[4];
return obj;
}
/// <summary>
/// Parse a string into a ReadWriteBinaryFile
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ReadWriteBinaryFile on success, null on error</returns>
private static ReadWriteBinaryFile ParseReadWriteBinaryFile(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new ReadWriteBinaryFile();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.FilePathname = parts[1];
if (parts.Length > 2)
obj.VariableName = parts[2];
if (parts.Length > 3 && int.TryParse(parts[3], out int fileOffset))
obj.FileOffset = fileOffset;
if (parts.Length > 4 && int.TryParse(parts[4], out int maxLength))
obj.MaxLength = maxLength;
return obj;
}
/// <summary>
/// Parse a string into a SetVariable
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled SetVariable on success, null on error</returns>
private static SetVariable ParseSetVariable(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new SetVariable();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.Value = parts[2];
return obj;
}
/// <summary>
/// Parse a string into a GetEnvironmentVariable
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled GetEnvironmentVariable on success, null on error</returns>
private static GetEnvironmentVariable ParseGetEnvironmentVariable(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new GetEnvironmentVariable();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.Environment = parts[2];
if (parts.Length > 3)
obj.DefaultValue = parts[3];
return obj;
}
/// <summary>
/// Parse a string into a CheckIfFileDirExists
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled CheckIfFileDirExists on success, null on error</returns>
private static CheckIfFileDirExists ParseCheckIfFileDirExists(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new CheckIfFileDirExists();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Pathname = parts[1];
if (parts.Length > 2)
obj.Message = parts[2];
if (parts.Length > 3)
obj.Title = parts[3];
return obj;
}
/// <summary>
/// Parse a string into a SetFileAttributes
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled SetFileAttributes on success, null on error</returns>
private static SetFileAttributes ParseSetFileAttributes(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new SetFileAttributes();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.FilePathname = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a SetFilesBuffers
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled SetFilesBuffers on success, null on error</returns>
private static SetFilesBuffers ParseSetFilesBuffers(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new SetFilesBuffers();
if (parts.Length > 0)
obj.MinimumFiles = parts[0];
if (parts.Length > 1)
obj.MinimumBuffers = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a FindFileInPath
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled FindFileInPath on success, null on error</returns>
private static FindFileInPath ParseFindFileInPath(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new FindFileInPath();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.VariableName = parts[1];
if (parts.Length > 2)
obj.FileName = parts[2];
if (parts.Length > 3)
obj.DefaultValue = parts[3];
if (parts.Length > 4)
obj.SearchDirectories = parts[4];
if (parts.Length > 5)
obj.Description = parts[5];
return obj;
}
/// <summary>
/// Parse a string into a CheckDiskSpace
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled CheckDiskSpace on success, null on error</returns>
private static CheckDiskSpace ParseCheckDiskSpace(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new CheckDiskSpace();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.ReserveSpace = parts[1];
if (parts.Length > 2)
obj.StatusVariable = parts[2];
if (parts.Length > 3)
obj.ComponentVariables = parts[3];
return obj;
}
/// <summary>
/// Parse a string into a InsertLineIntoTextFile
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled InsertLineIntoTextFile on success, null on error</returns>
private static InsertLineIntoTextFile ParseInsertLineIntoTextFile(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new InsertLineIntoTextFile();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.FileToEdit = parts[1];
if (parts.Length > 2)
obj.TextToInsert = parts[2];
if (parts.Length > 3)
obj.SearchForText = parts[3];
if (parts.Length > 4)
obj.CommentText = parts[4];
if (parts.Length > 5 && int.TryParse(parts[5], out int lineNumber))
obj.LineNumber = lineNumber;
return obj;
}
/// <summary>
/// Parse a string into a ParseString
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ParseString on success, null on error</returns>
private static ParseString ParseParseString(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new ParseString();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Source = parts[1];
if (parts.Length > 2)
obj.PatternPosition = parts[2];
if (parts.Length > 3)
obj.DestinationVariable1 = parts[3];
if (parts.Length > 4)
obj.DestinationVariable2 = parts[4];
return obj;
}
/// <summary>
/// Parse a string into a ExitInstallation
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ExitInstallation on success, null on error</returns>
private static ExitInstallation ParseExitInstallation(string data)
{
return new ExitInstallation();
}
/// <summary>
/// Parse a string into a SelfRegisterOCXsDLLs
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled SelfRegisterOCXsDLLs on success, null on error</returns>
private static SelfRegisterOCXsDLLs ParseSelfRegisterOCXsDLLs(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new SelfRegisterOCXsDLLs();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Description = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a InstallDirectXComponents
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled InstallDirectXComponents on success, null on error</returns>
private static InstallDirectXComponents ParseInstallDirectXComponents(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new InstallDirectXComponents();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.RootPath = parts[1];
if (parts.Length > 2)
obj.LibraryPath = parts[2];
if (parts.Length > 3 && int.TryParse(parts[3], out int sizeOrOffsetOrFlag))
obj.SizeOrOffsetOrFlag = sizeOrOffsetOrFlag;
return obj;
}
/// <summary>
/// Parse a string into a WizardBlockLoop
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled WizardBlockLoop on success, null on error</returns>
private static WizardBlockLoop ParseWizardBlockLoop(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new WizardBlockLoop();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
// TODO: This needs to be fixed when the model is updated
if (parts.Length > 1)
obj.DirectionVariable = parts[1];
if (parts.Length > 2)
obj.DisplayVariable = parts[2];
if (parts.Length > 3 && int.TryParse(parts[3], out int xPosition))
obj.XPosition = xPosition;
if (parts.Length > 4 && int.TryParse(parts[4], out int yPosition))
obj.YPosition = yPosition;
if (parts.Length > 5 && int.TryParse(parts[5], out int fillerColor))
obj.FillerColor = fillerColor;
if (parts.Length > 6)
obj.Operand_6 = parts[6];
if (parts.Length > 7)
obj.Operand_7 = parts[7];
if (parts.Length > 8)
obj.Operand_8 = parts[8];
if (parts.Length > 9)
obj.DialogVariableValueCompare = parts[9];
return obj;
}
/// <summary>
/// Parse a string into a ReadUpdateTextFile
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ReadUpdateTextFile on success, null on error</returns>
private static ReadUpdateTextFile ParseReadUpdateTextFile(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new ReadUpdateTextFile();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.Variable = parts[1];
if (parts.Length > 2)
obj.Pathname = parts[2];
if (parts.Length > 3)
obj.LanguageStrings = parts[3];
return obj;
}
/// <summary>
/// Parse a string into a PostToHttpServer
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled PostToHttpServer on success, null on error</returns>
private static PostToHttpServer ParsePostToHttpServer(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new PostToHttpServer();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.URL = parts[1];
if (parts.Length > 2)
obj.PostData = parts[2];
return obj;
}
/// <summary>
/// Parse a string into a PromptForFilename
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled PromptForFilename on success, null on error</returns>
private static PromptForFilename ParsePromptForFilename(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new PromptForFilename();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte flags))
obj.DataFlags = flags;
if (parts.Length > 1)
obj.DestinationVariable = parts[1];
if (parts.Length > 2)
obj.DefaultExtension = parts[2];
if (parts.Length > 3)
obj.DialogTitle = parts[3];
if (parts.Length > 4)
obj.FilterList = parts[4];
return obj;
}
/// <summary>
/// Parse a string into a StartStopService
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled StartStopService on success, null on error</returns>
private static StartStopService ParseStartStopService(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new StartStopService();
if (parts.Length > 0 && byte.TryParse(parts[0], out byte operation))
obj.Operation = operation;
if (parts.Length > 1)
obj.ServiceName = parts[1];
return obj;
}
/// <summary>
/// Parse a string into a CheckHttpConnection
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled CheckHttpConnection on success, null on error</returns>
private static CheckHttpConnection ParseCheckHttpConnection(string data)
{
string[] parts = data.Split((char)0x7F);
var obj = new CheckHttpConnection();
if (parts.Length > 0)
obj.UrlToCheck = parts[0];
if (parts.Length > 1)
obj.Win32ErrorTextVariable = parts[1];
if (parts.Length > 2)
obj.Win32ErrorNumberVariable = parts[2];
if (parts.Length > 3)
obj.Win16ErrorTextVariable = parts[3];
if (parts.Length > 4)
obj.Win16ErrorNumberVariable = parts[4];
return obj;
}
/// <summary>
/// Parse a string into a ExternalDllCall
/// </summary>
/// <param name="data">0x7F-separated string to parse</param>
/// <returns>Filled ExternalDllCall on success, null on error</returns>
private static ExternalDllCall ParseExternalDllCall(string data)
{
var obj = new ExternalDllCall();
obj.Args = data.Split((char)0x7F);
return obj;
}
#endregion
#region Helpers
/// <summary>
/// Returns if a string may be a typical control code
/// </summary>
/// <param name="str">String to check</param>
/// <param name="strict">Indicates if control codes should always be checked</param>
/// <returns>True if the string probably represents a control code, false otherwise</returns>
private static bool IsTypicalControlCode(string str, bool strict)
{
// Safeguard against odd cases
if (str.Length == 0)
return strict;
char firstChar = str[0];
// If there is no worry about newline trickery
if (strict)
return firstChar >= (char)0x00 && firstChar <= (char)0x25;
if (firstChar < (char)0x0A)
return true;
else if (firstChar == (char)0x0A && str.Length == 1)
return true;
else if (firstChar > (char)0x0A && firstChar < (char)0x0D)
return true;
else if (firstChar == (char)0x0D && str.Length == 1)
return true;
else if (firstChar > (char)0x0D && firstChar < (char)0x20)
return true;
else if (firstChar > (char)0x20 && firstChar <= (char)0x25 && str.Length == 1)
return true;
return false;
}
/// <summary>
/// Parse a Stream into a DeflateEntry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled DeflateEntry on success, null on error</returns>
private static DeflateEntry ParseDeflateEntry(Stream data)
{
var obj = new DeflateEntry();
obj.DeflateStart = data.ReadUInt32LittleEndian();
obj.DeflateEnd = data.ReadUInt32LittleEndian();
obj.InflatedSize = data.ReadUInt32LittleEndian();
return obj;
}
#endregion
}
}