Compare commits

...

2 Commits

Author SHA1 Message Date
b4bcc904fa Inno Header v6 reading almost works 2025-05-16 18:42:57 +00:00
53c4c16cd0 LZMA decoder allows repeated calls to Code 2025-05-16 18:40:48 +00:00
23 changed files with 813 additions and 76 deletions

84
IInnoHeader.cs Normal file
View File

@@ -0,0 +1,84 @@
using System;
namespace InnoPatcher;
internal interface IInnoHeader : IInnoSerializable
{
string? AppName { get; set; }
string? AppVerName { get; set; }
string? AppId { get; set; }
string? AppCopyright { get; set; }
string? AppPublisher { get; set; }
string? AppPublisherURL { get; set; }
string? AppSupportPhone { get; set; }
string? AppSupportURL { get; set; }
string? AppUpdatesURL { get; set; }
string? AppVersion { get; set; }
string? DefaultDirName { get; set; }
string? DefaultGroupName { get; set; }
string? BaseFilename { get; set; }
string? UninstallFilesDir { get; set; }
string? UninstallDisplayName { get; set; }
string? UninstallDisplayIcon { get; set; }
string? AppMutex { get; set; }
string? DefaultUserInfoName { get; set; }
string? DefaultUserInfoOrg { get; set; }
string? DefaultUserInfoSerial { get; set; }
string? AppReadmeFile { get; set; }
string? AppContact { get; set; }
string? AppComments { get; set; }
string? AppModifyPath { get; set; }
string? CreateUninstallRegKey { get; set; }
string? Uninstallable { get; set; }
string? CloseApplicationsFilter { get; set; }
string? SetupMutex { get; set; }
string? ChangesEnvironment { get; set; }
string? ChangesAssociations { get; set; }
ReadOnlySpan<byte> LicenseText { get; set; }
ReadOnlySpan<byte> InfoBeforeText { get; set; }
ReadOnlySpan<byte> InfoAfterText { get; set; }
ReadOnlySpan<byte> CompiledCodeText { get; set; }
int NumLanguageEntries { get; set; }
int NumCustomMessageEntries { get; set; }
int NumPermissionEntries { get; set; }
int NumTypeEntries { get; set; }
int NumComponentEntries { get; set; }
int NumTaskEntries { get; set; }
int NumDirEntries { get; set; }
int NumFileEntries { get; set; }
int NumFileLocationEntries { get; set; }
int NumIconEntries { get; set; }
int NumIniEntries { get; set; }
int NumRegistryEntries { get; set; }
int NumInstallDeleteEntries { get; set; }
int NumUninstallDeleteEntries { get; set; }
int NumRunEntries { get; set; }
int NumUninstallRunEntries { get; set; }
InnoVersionData MinVersion { get; set; }
InnoVersionData OnlyBelowVersion { get; set; }
int BackColor { get; set; }
int BackColor2 { get; set; }
InnoWizardStyle WizardStyle { get; set; }
int WizardSizePercentX { get; set; }
int WizardSizePercentY { get; set; }
InnoAlphaFormat WizardImageAlphaFormat { get; set; }
InnoSha1Digest PasswordHash { get; set; }
InnoSalt PasswordSalt { get; set; }
long ExtraDiskSpaceRequired { get; set; }
int SlicesPerDisk { get; set; }
InnoLogMode UninstallLogMode { get; set; }
InnoAutoNoYes DirExistsWarning { get; set; }
InnoPrivilegesRequired PrivilegesRequired { get; set; }
InnoPrivilegesRequiredOverride PrivilegesRequiredOverridesAllowed { get; set; }
InnoYesNoAuto ShowLanguageDialog { get; set; }
InnoLanguageDetectionMethod LanguageDetectionMethod { get; set; }
InnoCompressMethod CompressMethod { get; set; }
InnoProcessorArchitecture ArchitecturesAllowed { get; set; }
InnoProcessorArchitecture ArchitecturesInstallIn64BitMode { get; set; }
InnoDisablePage DisableDirPage { get; set; }
InnoDisablePage DisableProgramGroupPage { get; set; }
long UninstallDisplaySize { get; set; }
InnoHeaderOption Options { get; set; }
}

21
IInnoSerializable.cs Normal file
View File

@@ -0,0 +1,21 @@
using System;
namespace InnoPatcher;
/// <summary>
/// Serializable Inno structure.
/// </summary>
internal interface IInnoSerializable {
int StringCount { get; }
int ByteArrayCount { get; }
int PrimitiveSize { get; }
string? GetString(int idx);
void SetString(int idx, string? value);
ReadOnlySpan<byte> GetByteArray(int idx);
void SetByteArray(int idx, ReadOnlySpan<byte> value);
ReadOnlySpan<byte> GetPrimitive();
void SetPrimitive(ReadOnlySpan<byte> raw);
}

6
InnoAlphaFormat.cs Normal file
View File

@@ -0,0 +1,6 @@
namespace InnoPatcher;
internal enum InnoAlphaFormat {
// Must be same as Graphics.TAlphaFormat
Ignored, Defined, Premultiplied
}

5
InnoAutoNoYes.cs Normal file
View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoAutoNoYes {
Auto, No, Yes
}

13
InnoCompressMethod.cs Normal file
View File

@@ -0,0 +1,13 @@
namespace InnoPatcher;
internal enum InnoCompressMethod {
Stored, Zip, Bzip, LZMA, LZMA2
}
internal enum InnoCompressMethod4205 {
Stored, Bzip, LZMA
}
internal enum InnoCompressMethodPre4205 {
Zip, Bzip, LZMA
}

5
InnoDisablePage.cs Normal file
View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoDisablePage {
Auto, No, Yes
}

30
InnoHeaderOption.cs Normal file
View File

@@ -0,0 +1,30 @@
namespace InnoPatcher;
[Flags]
public enum InnoHeaderOption {
DisableStartupPrompt = 0x000001,
Uninstallable = 0x000002,
CreateAppDir = 0x000004,
DisableDirPage = 0x000008,
DisableProgramGroupPage = 0x000010,
AllowNoIcons = 0x000020,
AlwaysRestart = 0x000040,
AlwaysUsePersonalGroup = 0x000080,
WindowVisible = 0x000100,
WindowShowCaption = 0x000200,
WindowResizable = 0x000400,
WindowStartMaximized = 0x000800,
EnableDirDoesntExistWarning = 0x001000,
DisableAppendDir = 0x002000,
Password = 0x004000,
AllowRootDirectory = 0x008000,
DisableFinishedPage = 0x010000,
AdminPrivilegesRequired = 0x020000,
AlwaysCreateUninstallIcon = 0x040000,
ChangesAssociations = 0x080000,
CreateUninstallRegKey = 0x100000,
UsePreviousAppDir = 0x200000,
BackColorHorizontal = 0x400000,
UsePreviousGroup = 0x800000,
UpdateUninstallLogAppName = 0x1000000,
}

236
InnoHeaderV6.cs Normal file
View File

@@ -0,0 +1,236 @@
using System;
using System.Runtime.InteropServices;
namespace InnoPatcher;
sealed class InnoHeaderV6 : IInnoHeader, IInnoSerializable
{
public string? AppName { get; set; }
public string? AppVerName { get; set; }
public string? AppId { get; set; }
public string? AppCopyright { get; set; }
public string? AppPublisher { get; set; }
public string? AppPublisherURL { get; set; }
public string? AppSupportPhone { get; set; }
public string? AppSupportURL { get; set; }
public string? AppUpdatesURL { get; set; }
public string? AppVersion { get; set; }
public string? DefaultDirName { get; set; }
public string? DefaultGroupName { get; set; }
public string? BaseFilename { get; set; }
public string? UninstallFilesDir { get; set; }
public string? UninstallDisplayName { get; set; }
public string? UninstallDisplayIcon { get; set; }
public string? AppMutex { get; set; }
public string? DefaultUserInfoName { get; set; }
public string? DefaultUserInfoOrg { get; set; }
public string? DefaultUserInfoSerial { get; set; }
public string? AppReadmeFile { get; set; }
public string? AppContact { get; set; }
public string? AppComments { get; set; }
public string? AppModifyPath { get; set; }
public string? CreateUninstallRegKey { get; set; }
public string? Uninstallable { get; set; }
public string? CloseApplicationsFilter { get; set; }
public string? SetupMutex { get; set; }
public string? ChangesEnvironment { get; set; }
public string? ChangesAssociations { get; set; }
private byte[]? _licenseText;
public ReadOnlySpan<byte> LicenseText { get => _licenseText; set { _licenseText = value.ToArray(); } }
private byte[]? _infoBeforeText;
public ReadOnlySpan<byte> InfoBeforeText { get => _infoBeforeText; set { _infoBeforeText = value.ToArray(); } }
private byte[]? _infoAfterText;
public ReadOnlySpan<byte> InfoAfterText { get => _infoAfterText; set { _infoAfterText = value.ToArray(); } }
private byte[]? _compiledCodeText;
public ReadOnlySpan<byte> CompiledCodeText { get => _compiledCodeText; set { _compiledCodeText = value.ToArray(); } }
private Primitives _primitives;
public int NumLanguageEntries { get => _primitives.NumLanguageEntries; set => _primitives.NumLanguageEntries = value; }
public int NumCustomMessageEntries { get => _primitives.NumCustomMessageEntries; set => _primitives.NumCustomMessageEntries = value; }
public int NumPermissionEntries { get => _primitives.NumPermissionEntries; set => _primitives.NumPermissionEntries = value; }
public int NumTypeEntries { get => _primitives.NumTypeEntries; set => _primitives.NumTypeEntries = value; }
public int NumComponentEntries { get => _primitives.NumComponentEntries; set => _primitives.NumComponentEntries = value; }
public int NumTaskEntries { get => _primitives.NumTaskEntries; set => _primitives.NumTaskEntries = value; }
public int NumDirEntries { get => _primitives.NumDirEntries; set => _primitives.NumDirEntries = value; }
public int NumFileEntries { get => _primitives.NumFileEntries; set => _primitives.NumFileEntries = value; }
public int NumFileLocationEntries { get => _primitives.NumFileLocationEntries; set => _primitives.NumFileLocationEntries = value; }
public int NumIconEntries { get => _primitives.NumIconEntries; set => _primitives.NumIconEntries = value; }
public int NumIniEntries { get => _primitives.NumIniEntries; set => _primitives.NumIniEntries = value; }
public int NumRegistryEntries { get => _primitives.NumRegistryEntries; set => _primitives.NumRegistryEntries = value; }
public int NumInstallDeleteEntries { get => _primitives.NumInstallDeleteEntries; set => _primitives.NumInstallDeleteEntries = value; }
public int NumUninstallDeleteEntries { get => _primitives.NumUninstallDeleteEntries; set => _primitives.NumUninstallDeleteEntries = value; }
public int NumRunEntries { get => _primitives.NumRunEntries; set => _primitives.NumRunEntries = value; }
public int NumUninstallRunEntries { get => _primitives.NumUninstallRunEntries; set => _primitives.NumUninstallRunEntries = value; }
public InnoVersionData MinVersion { get => _primitives.MinVersion; set => _primitives.MinVersion = value; }
public InnoVersionData OnlyBelowVersion { get => _primitives.OnlyBelowVersion; set => _primitives.OnlyBelowVersion = value; }
public int BackColor { get => _primitives.BackColor; set => _primitives.BackColor = value; }
public int BackColor2 { get => _primitives.BackColor2; set => _primitives.BackColor2 = value; }
public InnoWizardStyle WizardStyle { get => _primitives.WizardStyle; set => _primitives.WizardStyle = value; }
public int WizardSizePercentX { get => _primitives.WizardSizePercentX; set => _primitives.WizardSizePercentX = value; }
public int WizardSizePercentY { get => _primitives.WizardSizePercentY; set => _primitives.WizardSizePercentY = value; }
public InnoAlphaFormat WizardImageAlphaFormat { get => _primitives.WizardImageAlphaFormat; set => _primitives.WizardImageAlphaFormat = value; }
public InnoSha1Digest PasswordHash { get => _primitives.PasswordHash; set => _primitives.PasswordHash = value; }
public InnoSalt PasswordSalt { get => _primitives.PasswordSalt; set => _primitives.PasswordSalt = value; }
public long ExtraDiskSpaceRequired { get => _primitives.ExtraDiskSpaceRequired; set => _primitives.ExtraDiskSpaceRequired = value; }
public int SlicesPerDisk { get => _primitives.SlicesPerDisk; set => _primitives.SlicesPerDisk = value; }
public InnoLogMode UninstallLogMode { get => _primitives.UninstallLogMode; set => _primitives.UninstallLogMode = value; }
public InnoAutoNoYes DirExistsWarning { get => _primitives.DirExistsWarning; set => _primitives.DirExistsWarning = value; }
public InnoPrivilegesRequired PrivilegesRequired { get => _primitives.PrivilegesRequired; set => _primitives.PrivilegesRequired = value; }
public InnoPrivilegesRequiredOverride PrivilegesRequiredOverridesAllowed { get => _primitives.PrivilegesRequiredOverridesAllowed; set => _primitives.PrivilegesRequiredOverridesAllowed = value; }
public InnoYesNoAuto ShowLanguageDialog { get => _primitives.ShowLanguageDialog; set => _primitives.ShowLanguageDialog = value; }
public InnoLanguageDetectionMethod LanguageDetectionMethod { get => _primitives.LanguageDetectionMethod; set => _primitives.LanguageDetectionMethod = value; }
public InnoCompressMethod CompressMethod { get => _primitives.CompressMethod; set => _primitives.CompressMethod = value; }
public InnoProcessorArchitecture ArchitecturesAllowed { get => _primitives.ArchitecturesAllowed; set => _primitives.ArchitecturesAllowed = value; }
public InnoProcessorArchitecture ArchitecturesInstallIn64BitMode { get => _primitives.ArchitecturesInstallIn64BitMode; set => _primitives.ArchitecturesInstallIn64BitMode = value; }
public InnoDisablePage DisableDirPage { get => _primitives.DisableDirPage; set => _primitives.DisableDirPage = value; }
public InnoDisablePage DisableProgramGroupPage { get => _primitives.DisableProgramGroupPage; set => _primitives.DisableProgramGroupPage = value; }
public long UninstallDisplaySize { get => _primitives.UninstallDisplaySize; set => _primitives.UninstallDisplaySize = value; }
public InnoHeaderOption Options { get => _primitives.Options; set => _primitives.Options = value; }
#region IInnoSerializable Members
public int StringCount => 30;
public int ByteArrayCount => 4;
public int PrimitiveSize => Marshal.SizeOf<Primitives>();
public string? GetString(int idx)
{
switch (idx)
{
case 0: return AppName;
case 1: return AppVerName;
case 2: return AppId;
case 3: return AppCopyright;
case 4: return AppPublisher;
case 5: return AppPublisherURL;
case 6: return AppSupportPhone;
case 7: return AppSupportURL;
case 8: return AppUpdatesURL;
case 9: return AppVersion;
case 10: return DefaultDirName;
case 11: return DefaultGroupName;
case 12: return BaseFilename;
case 13: return UninstallFilesDir;
case 14: return UninstallDisplayName;
case 15: return UninstallDisplayIcon;
case 16: return AppMutex;
case 17: return DefaultUserInfoName;
case 18: return DefaultUserInfoOrg;
case 19: return DefaultUserInfoSerial;
case 20: return AppReadmeFile;
case 21: return AppContact;
case 22: return AppComments;
case 23: return AppModifyPath;
case 24: return CreateUninstallRegKey;
case 25: return Uninstallable;
case 26: return CloseApplicationsFilter;
case 27: return SetupMutex;
case 28: return ChangesEnvironment;
case 29: return ChangesAssociations;
default: throw new ArgumentOutOfRangeException();
}
}
public void SetString(int idx, string? value)
{
switch (idx)
{
case 0: AppName = value; break;
case 1: AppVerName = value; break;
case 2: AppId = value; break;
case 3: AppCopyright = value; break;
case 4: AppPublisher = value; break;
case 5: AppPublisherURL = value; break;
case 6: AppSupportPhone = value; break;
case 7: AppSupportURL = value; break;
case 8: AppUpdatesURL = value; break;
case 9: AppVersion = value; break;
case 10: DefaultDirName = value; break;
case 11: DefaultGroupName = value; break;
case 12: BaseFilename = value; break;
case 13: UninstallFilesDir = value; break;
case 14: UninstallDisplayName = value; break;
case 15: UninstallDisplayIcon = value; break;
case 16: AppMutex = value; break;
case 17: DefaultUserInfoName = value; break;
case 18: DefaultUserInfoOrg = value; break;
case 19: DefaultUserInfoSerial = value; break;
case 20: AppReadmeFile = value; break;
case 21: AppContact = value; break;
case 22: AppComments = value; break;
case 23: AppModifyPath = value; break;
case 24: CreateUninstallRegKey = value; break;
case 25: Uninstallable = value; break;
case 26: CloseApplicationsFilter = value; break;
case 27: SetupMutex = value; break;
case 28: ChangesEnvironment = value; break;
case 29: ChangesAssociations = value; break;
default: throw new ArgumentOutOfRangeException();
}
}
public ReadOnlySpan<byte> GetByteArray(int idx)
{
switch (idx) {
case 0: return _licenseText;
case 1: return _infoBeforeText;
case 2: return _infoAfterText;
case 3: return _compiledCodeText;
default: throw new ArgumentOutOfRangeException();
}
}
public void SetByteArray(int idx, ReadOnlySpan<byte> value)
{
switch (idx) {
case 0: _licenseText = value.ToArray(); break;
case 1: _infoBeforeText = value.ToArray(); break;
case 2: _infoAfterText = value.ToArray(); break;
case 3: _compiledCodeText = value.ToArray(); break;
default: throw new ArgumentOutOfRangeException();
}
}
public ReadOnlySpan<byte> GetPrimitive()
{
return MemoryMarshal.Cast<Primitives, byte>(new ReadOnlySpan<Primitives>(ref _primitives));
}
public void SetPrimitive(ReadOnlySpan<byte> raw)
{
_primitives = MemoryMarshal.Cast<byte, Primitives>(raw)[0];
}
#endregion
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Primitives {
internal int NumLanguageEntries, NumCustomMessageEntries, NumPermissionEntries,
NumTypeEntries, NumComponentEntries, NumTaskEntries, NumDirEntries,
NumFileEntries, NumFileLocationEntries, NumIconEntries, NumIniEntries,
NumRegistryEntries, NumInstallDeleteEntries, NumUninstallDeleteEntries,
NumRunEntries, NumUninstallRunEntries; // Integer
internal InnoVersionData MinVersion, OnlyBelowVersion;
internal int BackColor, BackColor2; // Longint
internal InnoWizardStyle WizardStyle;
internal int WizardSizePercentX, WizardSizePercentY; // Integer
internal InnoAlphaFormat WizardImageAlphaFormat;
internal InnoSha1Digest PasswordHash;
internal InnoSalt PasswordSalt; // TSetupSalt
internal long ExtraDiskSpaceRequired; // Integer64
internal int SlicesPerDisk; // Integer
internal InnoLogMode UninstallLogMode;
internal InnoAutoNoYes DirExistsWarning;
internal InnoPrivilegesRequired PrivilegesRequired;
internal InnoPrivilegesRequiredOverride PrivilegesRequiredOverridesAllowed;
internal InnoYesNoAuto ShowLanguageDialog;
internal InnoLanguageDetectionMethod LanguageDetectionMethod;
internal InnoCompressMethod CompressMethod;
internal InnoProcessorArchitecture ArchitecturesAllowed, ArchitecturesInstallIn64BitMode;
internal InnoDisablePage DisableDirPage, DisableProgramGroupPage;
internal long UninstallDisplaySize; // Integer64
internal InnoHeaderOption Options;
}
}

View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoLanguageDetectionMethod {
UILanguage, Locale, None
}

View File

@@ -70,7 +70,7 @@ sealed class InnoLoader {
public Version Version => _version; public Version Version => _version;
internal int Version2 => _version.Major * 1000 + _version.Minor * 100 + _version.Revision; internal int Version2 => _version.Major * 1000 + _version.Minor * 100 + _version.Build;
public bool IsUnicode => _isUnicode; public bool IsUnicode => _isUnicode;
@@ -78,7 +78,7 @@ sealed class InnoLoader {
public InnoSetup GetSetup() { public InnoSetup GetSetup() {
_file.Seek(_offsets.Offset0 + HeaderSize, SeekOrigin.Begin); _file.Seek(_offsets.Offset0 + HeaderSize, SeekOrigin.Begin);
if (!InnoSetup.TryParse(_file, Version2, out InnoSetup result)) if (!InnoSetup.TryParse(_file, Version2, out InnoSetup? result))
throw new FormatException("Failed to parse Inno Setup"); throw new FormatException("Failed to parse Inno Setup");
return result; return result;
} }
@@ -199,7 +199,7 @@ sealed class InnoLoader {
} }
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnoLoaderOffsetTableV6 { struct InnoLoaderOffsetTableV6 {
internal uint TotalSize; // Minimum expected size of setup.exe internal uint TotalSize; // Minimum expected size of setup.exe
internal uint OffsetEXE; // Offset of compressed setup.e32 internal uint OffsetEXE; // Offset of compressed setup.e32
@@ -224,7 +224,7 @@ sealed class InnoLoader {
} }
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnoLoaderOffsetTableV7 { struct InnoLoaderOffsetTableV7 {
internal uint TotalSize; // Minimum expected size of setup.exe internal uint TotalSize; // Minimum expected size of setup.exe
internal uint OffsetEXE; // Offset of compressed setup.e32 internal uint OffsetEXE; // Offset of compressed setup.e32
@@ -248,7 +248,7 @@ sealed class InnoLoader {
} }
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnoLoaderOffsetTableV8 { struct InnoLoaderOffsetTableV8 {
internal uint Version; // SetupLdrOffsetTableVersion internal uint Version; // SetupLdrOffsetTableVersion
internal uint TotalSize; // Minimum expected size of setup.exe internal uint TotalSize; // Minimum expected size of setup.exe

5
InnoLogMode.cs Normal file
View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoLogMode {
Append, New, Overwrite
}

View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoPrivilegesRequired {
None, PowerUser, Admin, Lowest
}

View File

@@ -0,0 +1,7 @@
namespace InnoPatcher;
[Flags]
internal enum InnoPrivilegesRequiredOverride {
CommandLine = 0x01,
Dialog = 0x02,
}

View File

@@ -0,0 +1,10 @@
namespace InnoPatcher;
[Flags]
public enum InnoProcessorArchitecture {
Unknown = 0x01,
X86 = 0x02,
X64 = 0x04,
IA64 = 0x08,
ARM64 = 0x10,
}

9
InnoSalt.cs Normal file
View File

@@ -0,0 +1,9 @@
using System;
using System.Runtime.InteropServices;
namespace InnoPatcher;
[StructLayout(LayoutKind.Sequential)]
internal struct InnoSalt {
internal ulong _x0;
}

View File

@@ -1,18 +1,27 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SevenZip.Compression.LZMA; using SevenZip.Compression.LZMA;
using StringBuilder = System.Text.StringBuilder;
namespace InnoPatcher; namespace InnoPatcher;
sealed class InnoSetup { sealed class InnoSetup {
private const int BlockSize = 4096 + 4; private const int BlockSize = 4096 + 4;
private const int CompressionPropertiesLength = 5; private const int CompressionPropertiesLength = 5;
private readonly IInnoHeader _header;
private InnoSetup(IInnoHeader header) {
this._header = header;
}
internal static bool TryParse(Stream file, int innoVersion, [NotNullWhen(true)] out InnoSetup? result) { internal static bool TryParse(Stream file, int innoVersion, [NotNullWhen(true)] out InnoSetup? result) {
result = null; result = null;
byte[] headerB = new byte[Marshal.SizeOf<Header>()]; byte[] headerB = new byte[Marshal.SizeOf<BlocksHeader>()];
int read, offset = 0; int read, offset = 0;
while ((read = file.Read(headerB, offset, headerB.Length - offset)) != 0) while ((read = file.Read(headerB, offset, headerB.Length - offset)) != 0)
offset += read; offset += read;
@@ -20,56 +29,294 @@ sealed class InnoSetup {
return false; return false;
// Read header // Read header
Header header = MemoryMarshal.Cast<byte, Header>(headerB)[0]; BlocksHeader blocksHeader = MemoryMarshal.Cast<byte, BlocksHeader>(headerB)[0];
ReadOnlySpan<byte> headerB2 = headerB; ReadOnlySpan<byte> headerB2 = headerB;
if (!InnoLoader.VerifyCRC(headerB2.Slice(4), header.CRC)) if (!InnoLoader.VerifyCRC(headerB2.Slice(4), blocksHeader.CRC))
return false; return false;
if (header.Compressed != 0 && innoVersion < 4105) if (blocksHeader.Compressed != 0 && innoVersion < 4105)
throw new NotImplementedException("Zlib decompression not implemented."); throw new NotImplementedException("Zlib decompression not implemented.");
// Decode Stream data = new FixedBlockReadStream(file, blocksHeader.StoredSize);
Decoder decoder = new Decoder(); if (blocksHeader.Compressed != 0)
MemoryStream a = new MemoryStream(); data = new CompressedReadStream(data);
byte[] blockB = new byte[BlockSize];
bool decoderInitialized = false;
for (uint toRead = header.StoredSize; toRead > 0;) {
int blockSize = toRead > BlockSize ? BlockSize : (int)toRead;
offset = 0;
while ((read = file.Read(blockB, offset, blockSize - offset)) != 0)
offset += read;
if (offset < blockSize)
return false;
ReadOnlySpan<byte> blockData = blockB; // Parse
uint blockCRC = MemoryMarshal.Cast<byte, uint>(blockData.Slice(0, 4))[0]; InnoSerializer ser = new InnoSerializer();
blockData = blockData.Slice(4); IInnoHeader header = CreateHeader(innoVersion);
ser.Deserialize(data, header);
if (!InnoLoader.VerifyCRC(blockData, blockCRC))
return false;
if (header.Compressed != 0) { result = new InnoSetup(header);
if (!decoderInitialized) { return true;
decoder.SetDecoderProperties(blockData.Slice(0, CompressionPropertiesLength).ToArray());
blockData = blockData.Slice(CompressionPropertiesLength);
decoderInitialized = true;
}
decoder.Code(new MemoryStream(blockB, blockSize - blockData.Length, blockData.Length), a, blockData.Length, 84158, null);
} else
a.Write(blockData);
toRead -= (uint)blockSize;
}
result = null;
return false;
} }
[StructLayout(LayoutKind.Sequential, Pack = 1)] public string? AppName => _header.AppName;
struct Header { public string? AppVerName => _header.AppVerName;
public string? AppId => _header.AppId;
public string? AppCopyright => _header.AppCopyright;
public string? AppPublisher => _header.AppPublisher;
public string? AppPublisherURL => _header.AppPublisherURL;
public string? AppSupportPhone => _header.AppSupportPhone;
public string? AppSupportURL => _header.AppSupportURL;
public string? AppUpdatesURL => _header.AppUpdatesURL;
public string? AppVersion => _header.AppVersion;
private static IInnoHeader CreateHeader(int version) {
if (version >= 6000 && version <= 7000)
return new InnoHeaderV6();
else
throw new NotSupportedException($"Header format for version {version} is not supported.");
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BlocksHeader {
internal uint CRC; internal uint CRC;
internal uint StoredSize; // Total bytes written, including the CRCs internal uint StoredSize; // Total bytes written, including the CRCs
internal byte Compressed; // True if data is compressed, False if not internal byte Compressed; // True if data is compressed, False if not
} }
sealed class InnoSerializer {
private byte[] _buffer = new byte[512];
internal void Deserialize(Stream src, IInnoSerializable dst) {
StringBuilder sb = new StringBuilder();
for (int i = 0, j = dst.StringCount; i < j; i++) {
int byteLen = ReadInt32(src);
sb.Clear();
sb.EnsureCapacity(byteLen / 2);
ReadString(src, sb, byteLen);
dst.SetString(i, sb.ToString());
}
for (int i = 0, j = dst.ByteArrayCount; i < j; i++) {
int len = ReadInt32(src);
byte[] value = new byte[len];
ReadByteArray(src, value, len);
dst.SetByteArray(i, value);
}
int size = dst.PrimitiveSize;
if (_buffer.Length < size) {
int doubleSize = _buffer.Length * 2;
_buffer = new byte[doubleSize >= size ? doubleSize : size];
}
ReadByteArray(src, _buffer, size);
ReadOnlySpan<byte> buffer = _buffer;
dst.SetPrimitive(buffer.Slice(0, size));
}
private int ReadInt32(Stream a) {
int read, offset = 0;
byte[] buffer = _buffer;
while (offset < 4 && (read = a.Read(buffer, offset, 4 - offset)) != 0)
offset += read;
if (offset < 4)
throw new EndOfStreamException();
ReadOnlySpan<byte> buffer2 = buffer;
return MemoryMarshal.Cast<byte, int>(buffer2.Slice(0, 4))[0];
}
private void ReadString(Stream src, StringBuilder dst, int byteLen) {
int read;
byte[] buffer = _buffer;
ReadOnlySpan<char> bufferC = MemoryMarshal.Cast<byte, char>(buffer);
bool splitChar = false;
while (byteLen != 0 && (read = src.Read(buffer, splitChar ? 1 : 0, Math.Min(byteLen, buffer.Length))) != 0) {
if (splitChar)
read++;
dst.Append(bufferC.Slice(0, read / 2));
splitChar = (read & 1) != 0;
if (splitChar) {
buffer[0] = buffer[read - 1];
read--;
}
byteLen -= read;
}
if (byteLen != 0)
throw new EndOfStreamException();
}
private void ReadByteArray(Stream src, byte[] dst, int len) {
int read, offset = 0;
while (offset < len && (read = src.Read(dst, offset, len - offset)) != 0)
offset += read;
if (offset != len)
throw new EndOfStreamException();
}
}
/// <summary>
/// Reads underlying stream composed of fixed-sized CRC protected blocks.
/// </summary>
sealed class FixedBlockReadStream : Stream {
private readonly Stream _inner;
private readonly byte[] _buffer;
private int _bufferOffset;
private int _bufferLeft;
private uint _innerLeft;
internal FixedBlockReadStream(Stream inner, uint innerLength) {
this._inner = inner;
this._innerLeft = innerLength;
this._buffer = new byte[BlockSize];
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override int Read(byte[] buffer, int offset, int count) {
if (buffer == null)
throw new ArgumentNullException();
if (offset < 0 || count < 0 || buffer.Length - offset < count)
throw new ArgumentOutOfRangeException();
int result = 0;
while (count > 0) {
if (_bufferLeft == 0) {
if (_innerLeft == 0)
return result;
ReadBlock();
}
int toCopy = count;
if (toCopy > _bufferLeft)
toCopy = _bufferLeft;
Buffer.BlockCopy(_buffer, _bufferOffset, buffer, offset, toCopy);
_bufferOffset += toCopy;
_bufferLeft -= toCopy;
offset += toCopy;
count -= toCopy;
result += toCopy;
}
return result;
}
public override int ReadByte() {
if (_bufferLeft == 0) {
if (_innerLeft == 0)
return -1;
ReadBlock();
}
_bufferLeft--;
return _buffer[_bufferOffset++];
}
private void ReadBlock() {
if (_bufferLeft != 0)
throw new InvalidOperationException();
int blockSize = _innerLeft > BlockSize ? BlockSize : (int)_innerLeft;
int read, offset = 0;
while ((read = _inner.Read(_buffer, offset, blockSize - offset)) != 0)
offset += read;
if (offset < blockSize)
throw new IOException();
ReadOnlySpan<byte> blockData = _buffer;
uint blockCRC = MemoryMarshal.Cast<byte, uint>(blockData.Slice(0, 4))[0];
blockData = blockData.Slice(4);
if (!InnoLoader.VerifyCRC(blockData, blockCRC))
throw new IOException();
_bufferOffset = 4; // skip CRC
_bufferLeft = blockSize - 4; // minus CRC
}
public override void Write(byte[] buffer, int offset, int count) {
throw new NotSupportedException();
}
public override void SetLength(long value) {
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin) {
throw new NotSupportedException();
}
public override void Flush() {
throw new NotSupportedException();
}
}
/// <summary>
/// Reads underlying LZMA compressed stream and decompresses it.
/// </summary>
sealed class CompressedReadStream : Stream {
private readonly Stream _inner;
private readonly Decoder _decoder;
private byte[]? _singleByteBuffer;
private MemoryStream? _singleByteStream;
private long _outOffset;
internal CompressedReadStream(Stream inner) {
this._inner = inner;
this._decoder = new Decoder();
byte[] props = new byte[CompressionPropertiesLength];
int read, offset = 0;
while ((read = inner.Read(props, offset, props.Length - offset)) != 0)
offset += read;
if (offset != props.Length)
throw new IOException();
_decoder.SetDecoderProperties(props);
_decoder.Code(inner, Stream.Null, -1, 0, null, noInit: false, isLast: false);
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override int Read(byte[] buffer, int offset, int count) {
if (buffer == null)
throw new ArgumentNullException();
if (offset < 0 || count < 0 || buffer.Length - offset < count)
throw new ArgumentOutOfRangeException();
MemoryStream result = new MemoryStream(buffer, offset, count, writable: true);
_decoder.Code(_inner, result, -1, _outOffset + count, null, noInit: true, isLast: false);
int read = (int)result.Position;
_outOffset += read;
return read;
}
public override int ReadByte() {
if (_singleByteBuffer == null || _singleByteStream == null) {
_singleByteBuffer = new byte[1];
_singleByteStream = new MemoryStream(_singleByteBuffer, 0, 1, writable: true);
}
_singleByteStream.SetLength(0);
_decoder.Code(_inner, _singleByteStream, -1, _outOffset + 1, null, noInit: true, isLast: false);
int read = (int)_singleByteStream.Position;
_outOffset += read;
return read == 0 ? -1 : _singleByteBuffer[0];
}
public override void Write(byte[] buffer, int offset, int count) {
throw new NotSupportedException();
}
public override void SetLength(long value) {
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin) {
throw new NotSupportedException();
}
public override void Flush() {
throw new NotSupportedException();
}
}
} }

9
InnoSha1Digest.cs Normal file
View File

@@ -0,0 +1,9 @@
using System;
using System.Runtime.InteropServices;
namespace InnoPatcher;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct InnoSha1Digest {
internal uint _x0, _x1, _x2, _x3, _x4;
}

10
InnoVersionData.cs Normal file
View File

@@ -0,0 +1,10 @@
using System;
using System.Runtime.InteropServices;
namespace InnoPatcher;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct InnoVersionData {
int WinVersion, NTVersion; // Cardinal
ushort NTServicePack; // Word
}

5
InnoWizardStyle.cs Normal file
View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoWizardStyle {
Classic, Modern
}

5
InnoYesNoAuto.cs Normal file
View File

@@ -0,0 +1,5 @@
namespace InnoPatcher;
internal enum InnoYesNoAuto {
Yes, No, Auto
}

View File

@@ -6,7 +6,8 @@ namespace SevenZip.Compression.LZ
uint _pos; uint _pos;
uint _windowSize = 0; uint _windowSize = 0;
uint _streamPos; uint _streamPos;
System.IO.Stream _stream; uint _writePos;
internal System.IO.Stream _stream;
public uint TrainSize = 0; public uint TrainSize = 0;
@@ -20,6 +21,7 @@ namespace SevenZip.Compression.LZ
_windowSize = windowSize; _windowSize = windowSize;
_pos = 0; _pos = 0;
_streamPos = 0; _streamPos = 0;
_writePos = 0;
} }
public void Init(System.IO.Stream stream, bool solid) public void Init(System.IO.Stream stream, bool solid)
@@ -29,6 +31,7 @@ namespace SevenZip.Compression.LZ
if (!solid) if (!solid)
{ {
_streamPos = 0; _streamPos = 0;
_writePos = 0;
_pos = 0; _pos = 0;
TrainSize = 0; TrainSize = 0;
} }
@@ -40,7 +43,7 @@ namespace SevenZip.Compression.LZ
uint size = (len < _windowSize) ? (uint)len : _windowSize; uint size = (len < _windowSize) ? (uint)len : _windowSize;
TrainSize = size; TrainSize = size;
stream.Position = len - size; stream.Position = len - size;
_streamPos = _pos = 0; _streamPos = _pos = _writePos = 0;
while (size > 0) while (size > 0)
{ {
uint curSize = _windowSize - _pos; uint curSize = _windowSize - _pos;
@@ -52,8 +55,9 @@ namespace SevenZip.Compression.LZ
size -= (uint)numReadBytes; size -= (uint)numReadBytes;
_pos += (uint)numReadBytes; _pos += (uint)numReadBytes;
_streamPos += (uint)numReadBytes; _streamPos += (uint)numReadBytes;
_writePos += (uint)numReadBytes;
if (_pos == _windowSize) if (_pos == _windowSize)
_streamPos = _pos = 0; _streamPos = _pos = _writePos = 0;
} }
return true; return true;
} }
@@ -64,15 +68,19 @@ namespace SevenZip.Compression.LZ
_stream = null; _stream = null;
} }
public void Flush() public void Flush(bool writeOnly = false, int avoidNewestCount = 0)
{ {
uint size = _pos - _streamPos; int size = (int)(_pos - _writePos) - avoidNewestCount;
if (size == 0) if (size <= 0)
return; return;
_stream.Write(_buffer, (int)_streamPos, (int)size); _stream.Write(_buffer, (int)_writePos, (int)size);
if (_pos >= _windowSize) _writePos += (uint)size;
_pos = 0;
_streamPos = _pos; if (!writeOnly) {
if (_pos >= _windowSize)
_pos = 0;
_streamPos = _pos;
}
} }
public void CopyBlock(uint distance, uint len) public void CopyBlock(uint distance, uint len)

View File

@@ -4,7 +4,7 @@ namespace SevenZip.Compression.LZMA
{ {
using RangeCoder; using RangeCoder;
public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream public class Decoder //: ICoder, ISetDecoderProperties // ,System.IO.Stream
{ {
class LenDecoder class LenDecoder
{ {
@@ -225,26 +225,33 @@ namespace SevenZip.Compression.LZMA
m_PosAlignDecoder.Init(); m_PosAlignDecoder.Init();
} }
Base.State state;
uint rep0, rep1, rep2, rep3;
UInt64 nowPos64;
public void Code(System.IO.Stream inStream, System.IO.Stream outStream, public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress) Int64 inSize, Int64 outSize, ICodeProgress progress, bool noInit, bool isLast)
{ {
Init(inStream, outStream);
Base.State state = new Base.State();
state.Init();
uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
UInt64 nowPos64 = 0;
UInt64 outSize64 = (UInt64)outSize; UInt64 outSize64 = (UInt64)outSize;
if (nowPos64 < outSize64) if (!noInit) {
{ Init(inStream, outStream);
if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0) state.Init();
throw new DataErrorException(); rep0 = 0; rep1 = 0; rep2 = 0; rep3 = 0;
state.UpdateChar(); nowPos64 = 0;
byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
m_OutWindow.PutByte(b); if (nowPos64 < outSize64)
nowPos64++; {
if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0)
throw new DataErrorException();
state.UpdateChar();
byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
m_OutWindow.PutByte(b);
nowPos64++;
}
} else {
m_RangeDecoder.Stream = inStream;
m_OutWindow._stream = outStream;
} }
while (nowPos64 < outSize64) while (nowPos64 < outSize64)
{ {
// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64); // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
@@ -339,9 +346,13 @@ namespace SevenZip.Compression.LZMA
} }
} }
} }
m_OutWindow.Flush();
m_OutWindow.ReleaseStream(); if (isLast) {
m_RangeDecoder.ReleaseStream(); m_OutWindow.Flush();
m_OutWindow.ReleaseStream();
m_RangeDecoder.ReleaseStream();
} else
m_OutWindow.Flush(writeOnly: true, (int)(nowPos64 - outSize64));
} }
public void SetDecoderProperties(byte[] properties) public void SetDecoderProperties(byte[] properties)

View File

@@ -6,7 +6,8 @@ class Program {
InnoLoader loader = InnoLoader.FromPE(exe); InnoLoader loader = InnoLoader.FromPE(exe);
Console.WriteLine($"Inno Loader {loader.Version}" + (loader.IsUnicode ? " (Unicode)" : "")); Console.WriteLine($"Inno Loader {loader.Version}" + (loader.IsUnicode ? " (Unicode)" : ""));
loader.GetSetup(); InnoSetup setup = loader.GetSetup();
Console.WriteLine($"Inno Setup {setup.AppName} [{setup.AppVersion}] ({setup.AppId})");
} }
} }
} }