├── DelphiGrammar.pm ├── LICENSE ├── ParserGenerator.pm ├── README.md ├── Setup ├── Inno.pm └── Inno │ ├── BlockReader.pm │ ├── FieldReader.pm │ ├── Interpret.pm │ ├── Interpret2008.pm │ ├── Interpret4000.pm │ ├── Interpret4003.pm │ ├── Interpret4010.pm │ ├── Interpret4106.pm │ ├── Interpret5105.pm │ ├── Interpret5309.pm │ ├── LzmaReader.pm │ ├── Struct.pm │ ├── Struct1326.pm │ ├── Struct2019.pm │ ├── Struct3007.pm │ ├── Struct4008.pm │ ├── Struct4009.pm │ ├── Struct4010.pm │ ├── Struct4011.pm │ ├── Struct4100.pm │ ├── Struct4101.pm │ ├── Struct4102.pm │ ├── Struct4103.pm │ ├── Struct4104.pm │ ├── Struct4105.pm │ ├── Struct4106.pm │ ├── Struct4107.pm │ ├── Struct4108.pm │ ├── Struct4200.pm │ ├── Struct4201.pm │ ├── Struct4202.pm │ ├── Struct4203.pm │ ├── Struct4204.pm │ ├── Struct4205.pm │ ├── Struct4206.pm │ ├── Struct4207.pm │ ├── Struct5000.pm │ ├── Struct5001.pm │ ├── Struct5002.pm │ ├── Struct5003.pm │ ├── Struct5004.pm │ ├── Struct5005.pm │ ├── Struct5006.pm │ ├── Struct5007.pm │ ├── Struct5008.pm │ ├── Struct5100.pm │ ├── Struct5101.pm │ ├── Struct5102.pm │ ├── Struct5103.pm │ ├── Struct5104.pm │ ├── Struct5105.pm │ ├── Struct5106.pm │ ├── Struct5107.pm │ ├── Struct5108.pm │ ├── Struct5109.pm │ ├── Struct5110.pm │ ├── Struct5111.pm │ ├── Struct5112.pm │ ├── Struct5113.pm │ ├── Struct5114.pm │ ├── Struct5200.pm │ ├── Struct5201.pm │ ├── Struct5202.pm │ ├── Struct5203.pm │ ├── Struct5300.pm │ ├── Struct5300u.pm │ ├── Struct5301.pm │ ├── Struct5301u.pm │ ├── Struct5302.pm │ ├── Struct5302u.pm │ ├── Struct5303.pm │ ├── Struct5303u.pm │ ├── Struct5304.pm │ ├── Struct5304u.pm │ ├── Struct5305.pm │ ├── Struct5305u.pm │ ├── Struct5306.pm │ ├── Struct5306u.pm │ ├── Struct5307.pm │ ├── Struct5307u.pm │ ├── Struct5308.pm │ ├── Struct5308u.pm │ ├── Struct5309.pm │ ├── Struct5309u.pm │ ├── Struct5310.pm │ ├── Struct5310u.pm │ ├── Struct5311.pm │ ├── Struct5311u.pm │ ├── Struct5400.pm │ ├── Struct5400u.pm │ ├── Struct5401.pm │ ├── Struct5401u.pm │ ├── Struct5402.pm │ ├── Struct5402u.pm │ ├── Struct5403.pm │ ├── Struct5403u.pm │ ├── Struct5500.pm │ ├── Struct5500u.pm │ ├── Struct5501.pm │ ├── Struct5501u.pm │ ├── Struct5502.pm │ ├── Struct5502u.pm │ ├── Struct5503.pm │ ├── Struct5503u.pm │ ├── Struct5504.pm │ ├── Struct5504u.pm │ ├── Struct5505.pm │ ├── Struct5505u.pm │ ├── Struct5506.pm │ ├── Struct5506u.pm │ ├── Struct5507.pm │ ├── Struct5507u.pm │ ├── Struct5508.pm │ ├── Struct5508u.pm │ ├── Struct5509.pm │ ├── Struct5509u.pm │ ├── Struct5600.pm │ ├── Struct5600u.pm │ ├── Struct5601.pm │ ├── Struct5601u.pm │ ├── Struct6000.pm │ ├── Struct6000u.pm │ ├── Struct6001.pm │ ├── Struct6001u.pm │ ├── Struct6002.pm │ ├── Struct6002u.pm │ ├── Struct6003.pm │ ├── Struct6003u.pm │ ├── Struct6004.pm │ ├── Struct6004u.pm │ ├── Struct6005.pm │ ├── Struct6005u.pm │ ├── Struct6100.pm │ ├── Struct6100u.pm │ ├── Struct6101.pm │ ├── Struct6101u.pm │ ├── Struct6102.pm │ ├── Struct6102u.pm │ ├── Struct6200.pm │ ├── Struct6200u.pm │ ├── Struct6201.pm │ ├── Struct6201u.pm │ ├── Struct6202.pm │ └── Struct6202u.pm ├── Win ├── Exe.pm └── Exe │ ├── DosHeader.pm │ ├── OptionalHeader.pm │ ├── PeHeader.pm │ ├── Section.pm │ ├── Table.pm │ └── Util.pm ├── makeissrc.pl ├── makestruct.pl └── uninno.pl /DelphiGrammar.pm: -------------------------------------------------------------------------------- 1 | package DelphiGrammar; 2 | 3 | use strict; 4 | use Marpa::R2; 5 | 6 | our $GRAMMAR = <<'END_OF_SOURCE'; 7 | 8 | :start ::= 9 | :discard ~ 10 | :default ::= action => ::array bless => ::lhs 11 | 12 | ~ | 13 | ~ [\s]+ 14 | ~ '{' '}' | '(*' '*)' 15 | ~ [^}]* 16 | 17 | ~ ',' 18 | ~ ';' 19 | ~ '.' 20 | 21 | ~ [A-Za-z] 22 | ~ [0-9] 23 | ~ [0-9A-Fa-f] 24 | ~ [_] 25 | 26 | ~ 27 | ~ * 28 | ~ | 29 | ~ | 30 | 31 | ::= + separator => proper => 1 bless => list 32 | 33 | ~ + 34 | ~ + 35 | ~ | '$' 36 | ~ 37 | ~ '+' | '-' 38 | ~ 39 | ~ | '.' | | '.' 40 | ~ [Ee] | [Ee] 41 | ~ | 42 | ~ 43 | 44 | ::= + 45 | ~ | 46 | ~ ['] ['] 47 | ~ [^\n'] 48 | ~ * 49 | ~ '#' 50 | 51 | ::= ';' '.' 52 | ::= 'unit' bless => defer1 53 | 54 | ::= 'interface' | 'interface' 55 | ::= * bless => list 56 | ::= bless => defer0 | bless => defer0 57 | # ::= | | | 58 | 59 | ::= 'implementation' | 'implementation' 60 | # ::= 'implementation' | 'implementation' 61 | 62 | ::= 'end' bless => empty 63 | # ::= 'initialization' 'end' | 'end' 64 | 65 | ::= 'uses' ';' bless => defer1 66 | 67 | ::= + separator => proper => 1 bless => list 68 | 69 | ::= 'const' bless => defer1 70 | ::= + bless => list 71 | ::= bless => defer0 | bless => defer0 72 | 73 | ::= 'type' bless => defer1 74 | ::= + bless => list 75 | 76 | ::= '=' ';' 77 | ::= bless => defer0 78 | 79 | ::= | | '(' ')' | 'not' | | | |
80 | ::= bless => defer0 | bless => defer0 | bless => defer0 81 | ::= 82 | ::= * 83 | ::= '*' | '/' | 'div' | 'mod' | 'and' | 'shl' | 'shr' | 'as' 84 | ::= 85 | ::= * 86 | ::= '+' | '-' | 'or' | 'xor' 87 | ::= | 88 | ::= '<' | '<=' | '>' | '>=' | '=' | '<>' | 'in' | 'is' 89 | 90 | ::= ':' '=' ';' 91 | ::= bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 92 | # ::= |
| | | 93 | 94 | ::= '=' ';' 95 | ::= bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 96 | 97 | ::= bless => defer0 | bless => defer0 98 | ::= bless => defer0 99 | ::= 'Real' | 'Single' | 'Double' | 'Extended' | 'Comp' 100 | 101 | ::= bless => defer0 | bless => defer0 | bless => defer0 102 | ::= 'Integer' | 'ShortInt' | 'SmallInt' | 'Smallint' | 'LongInt' | 'Longint' | 'Byte' | 'Word' | 'Cardinal' | 'Boolean' | 'ByteBool' | 'WordBool' | 'LongBool' | 'Char' | 'AnsiChar' | 'WideChar' | 'Integer64' | 'LongWord' 103 | 104 | ::= '(' ')' 105 | 106 | ::= '..' 107 | 108 | ::= 'String' | 'String' '[' ']' | 'AnsiString' | 'AnsiString' '[' ']' | 'WideString' | 'WideString' '[' ']' 109 | 110 | ::= 'packed' | 111 | ::= bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 | bless => defer0 112 | 113 | ::= 'array' '[' ']' 'of' 114 | ::= + separator => proper => 1 bless => list 115 | 116 | ::= '^' | 'PChar' 117 | ::= bless => defer0 118 | 119 | ::= 'procedure' | 'procedure' | 'procedure' 'of' 'object' | 'procedure' 'of' 'object' | 'function' ':' | 'function' ':' | 'function' ':' 'of' 'object' | 'function' ':' 'of' 'object' 120 | 121 | ::= '(' ')' 122 | ::= + separator => semicolon proper => 1 bless => list 123 | ::= | 'var' | 'const' | ':' | 'var' ':' | 'const' ':' | ':' 'array' 'of' | 'var' ':' 'array' 'of' | 'const' ':' 'array' 'of' ; 124 | 125 | ::= bless => defer0 126 | ::= bless => defer0 127 | 128 | ::= 'record' 'end' | 'record' 'end' 129 | ::= | ';' | | ';' | ';' ';' | ';' 130 | ::= + separator => semicolon proper => 1 131 | ::= ':' 132 | ::= 'case' 'of' | 'case' ':' 'of' 133 | ::= bless => defer0 134 | ::= variant+ separator => semicolon proper => 1 bless => list 135 | ::= ':' '(' ')' 136 | ::= + separator => proper => 1 bless => list 137 | 138 | ::= 'set' 'of' 139 | 140 | ::= 'file' | 'file' 'of' 141 | 142 | ::= 'class' 'of' 143 | 144 | ::= '(' expression ')' 145 | 146 |
::= '@' | '@' | '@' 147 | 148 | ::= | | | | | 149 | ::= * bless => list 150 | 151 | ::= | | '^' 152 | ::= '[' ']' 153 | ::= + separator => proper => 1 bless => list 154 | ::= '.' 155 | 156 | ::= 157 | 158 | ::= '(' ')' 159 | 160 | ::= | 161 | ::= | | | 162 | ::= '(' ')' 163 | ::= + separator => proper => 1 bless => list 164 | ::= | 165 | 166 | ::= | '.' 167 | ::= '.' 168 | 169 | ::= '(' ')' bless => defer1 170 | ::= + separator => proper => 1 bless => list 171 | 172 | ::= '(' ')' 173 | ::= + separator => proper => 1 bless => list 174 | ::= ':' 175 | 176 | ::= | 'nil' 177 | 178 | END_OF_SOURCE 179 | 180 | sub G { 181 | my ($class, $action, $bless) = @_; 182 | $class = ref($class) if defined(ref($class)); 183 | if (defined($action) && $action !~ /^::/) { 184 | $bless = $action; 185 | undef($action); 186 | } 187 | return Marpa::R2::Scanless::G->new({ default_action => $action, source => \$GRAMMAR, bless_package => $bless }); 188 | } 189 | 190 | sub R { 191 | return Marpa::R2::Scanless::R->new({ grammar => shift->G(@_) }); 192 | } 193 | 194 | 1; 195 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Gregor Riepl 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uninno 2 | 3 | ## Introduction 4 | 5 | uninno is a portable unpacking tool for Inno Setup (IS) installers. 6 | It was originally conceived as a means to extract the data files from 7 | [GOG.com (Good Old Games)](https://www.gog.com/) installer packages. 8 | 9 | Back in 2008, when GOG launched, they only offered repackaged versions 10 | of older DOS and Windows games for modern Windows systems. 11 | Many of these games can be played with emulators or engine rewrites on 12 | a wide variety of systems, but this requires the game data in raw form. 13 | 14 | With the help of uninno, GOG customers can play such games without 15 | requiring access to a Windows system or Windows emulator. 16 | 17 | Recently, GOG also started supporting Linux and MacOS, and some games that 18 | come packaged with an emulator (such as ScummVM) can also be downloaded 19 | for non-Windows systems now. 20 | 21 | When new InnoSetup versions are released, support for them will 22 | occasionally be added to uninno. Note that uninno does not implement all 23 | features that installers may use. In particular, more recent GOG.com 24 | installers no longer work, because they incorporate custom code to 25 | process the unpacked files. 26 | 27 | ## Usage 28 | 29 | To just use uninno as a Inno Setup extractor, run the uninno.pl utility. 30 | 31 | $ ./uninno.pl setup.exe 32 | 33 | It will extract all files from the application part of the installer archvive 34 | and put them into ./app 35 | 36 | Various Perl modules are required. You can either install them from CPAN or 37 | using your package manager (if available). 38 | 39 | These are: 40 | * Switch 41 | * DateTime 42 | * Digest::CRC 43 | * IO::Uncompress::AnyInflate 44 | * IO::Uncompress::Bunzip2 45 | * Compress::Raw::Lzma 46 | * Crypt::RC4 47 | 48 | On Debian/Ubuntu Linux, use the following command to install all dependencies: 49 | 50 | $ sudo apt install libswitch-perl libdatetime-perl libdigest-crc-perl libcompress-raw-lzma-perl libtext-glob-perl libio-stringy-perl libcrypt-rc4-perl 51 | 52 | ## Code 53 | 54 | uninno consists of a bunch of Perl packages that handle different stages of the 55 | analysis and extractiong process. Dissection of the installer executable is 56 | provided by Win::Exe, which has some submodules for each of the PE's parts. 57 | Setup::Inno provides a frontend for parsing the setup.0 (metadata) and setup.1 58 | (compressed files) parts of the Inno Setup installer. 59 | 60 | To improve code reuse and facilitate handling of all the installer versions, 61 | all version-specific code is represented by a class hierarchy, starting with 62 | a base class containing mostly stubs, continuing with support for the first 63 | open source Inno Setup release (2.0.8), and going up to the most recent version. 64 | Each class in the hierarchy has override points that implement headers, 65 | decompression and custom handling required for this version or its descendants. 66 | 67 | Secondary to that is a set of data structure parsers for each exact Inno Setup 68 | version. These can be generated from the Inno source code, as outlined below. 69 | 70 | Version numbers are coded in 4 digits: x.y.z -> xyzz. The last version part has 71 | two digits and is zero padded. Example: 2.0.8 -> 2008 72 | 73 | Inno Setup has used many different compression algorithms in the course of its 74 | history, with varying degrees of compatibility with standard software. 75 | zlib, bzip2, LZMA and LZMA2 compression are supported by uninno, using 76 | corresponding Perl modules. 77 | LZMA compression uses a lot of memory currently and performance is poor, as 78 | the API of Compress::Raw::Lzma is very low-level and hard to use in a 79 | straight-forward way. A better implementation may be written in the future. 80 | 81 | Starting with support for Inno Setup 5.5.0, a new approach is used to create the 82 | various structure parsers. Using a custom Delphi grammar based on an old edition 83 | of the Delphi Language Guide, Projects/Struct.pas from the Inno source code 84 | is processed and dissected. Then, a Perl module is generated that can parse 85 | binary data represented by the data structures in that file. The grammar can be 86 | found in DelphiGrammar.pm, the code for the generator is in ParserGenerator.pm. 87 | 88 | To generate a new parser, you need the Perl module Marpa::R2. You also need the 89 | specific Inno Setup Struct.pas you would like to implement support for. 90 | The makeissrc.pl utility can clone the official git repository for you and 91 | optionally download and patch in older versions from their respective source 92 | files. It will put the repository into ./innosetup by default. 93 | The tool needs the tools wget, unzip and git to do its work. 94 | 95 | makestruct.pl can then be used to access this repository and to generate a 96 | new parser for a specific Inno Setup version. 97 | 98 | For example: 99 | 100 | $ ./makestruct.pl --src ./innosetup --version 5.5.0u 101 | 102 | Versions with a u at the end are Unicode versions, which means that all strings 103 | are interpreted as UTF-16. Non-Unicode installers used to have their strings 104 | stored in Windows code page 1252, but more recent installer may also specify 105 | the used code page in the data structures. Support for this has not been 106 | implemented yet. 107 | It is recommended to always generate Unicode and non-Unicode versions 108 | when creating a parser for a new version. 109 | Output will go to Struct5500u.pm in this case, which needs to be put into 110 | Setup/Inno/ to make Inno.pm find it. 111 | 112 | ## Links 113 | 114 | * Inno Setup: http://www.jrsoftware.org/isinfo.php 115 | * innounp: http://innounp.sourceforge.net/ 116 | * GOG.com: http://www.gog.com 117 | * Object Pascal Language Guide for Delphi: https://web.archive.org/web/20160313055504/http://portal.aauj.edu/portal_resources/downloads/programming/delphi_object_pascal_language_guide.pdf 118 | 119 | ## Copyright 120 | 121 | uninno and all its components are 122 | 123 | Copyright © 2012-2024 by Gregor Riepl 124 | All rights reserved. 125 | 126 | Redistribution and use in source and binary forms, with or without modification, 127 | are permitted provided that the following conditions are met: 128 | 129 | Redistributions of source code must retain the above copyright notice, 130 | this list of conditions and the following disclaimer. 131 | 132 | Redistributions in binary form must reproduce the above copyright notice, 133 | this list of conditions and the following disclaimer in the documentation 134 | and/or other materials provided with the distribution. 135 | 136 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 137 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 138 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 139 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 140 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 141 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 142 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 143 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 144 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 145 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 146 | 147 | The data extraction routines are generated based on the Inno Setup source code, 148 | while the rest of the software was developed independently. 149 | See http://www.jrsoftware.org/files/is/license.txt for the Inno Setup license. 150 | 151 | No part of the project is affiliated with Inno Setup or its authors. 152 | -------------------------------------------------------------------------------- /Setup/Inno.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno; 4 | 5 | use strict; 6 | use Switch 'Perl6'; 7 | use Fcntl; 8 | use IO::File; 9 | use Win::Exe; 10 | use Win::Exe::Util; 11 | use Setup::Inno::Interpret; 12 | use Carp; 13 | 14 | our $SetupLdrExeHeaderOffset = 0x30; 15 | our $SetupLdrExeHeaderID = 0x6F6E6E49; # 'Inno' 16 | our $SetupLdrOffsetTableResID = 11111; 17 | our $UninstallerMsgTailID = 0x67734D49; 18 | our $ExeHeaderLength = 8; 19 | our $DefaultUninstallExeName = "uninstall.exe"; 20 | our $DefaultRegSvrExeName = "regsvr.exe"; 21 | 22 | sub new { 23 | my ($class, $filename) = @_; 24 | 25 | my $self = { 26 | Input => IO::File->new($filename, '<') || croak("Can't open installer"), 27 | }; 28 | bless($self, $class); 29 | 30 | eval { 31 | $self->{Input}->seek($SetupLdrExeHeaderOffset, Fcntl::SEEK_SET) || croak("Can't seek to Inno header"); 32 | $self->{Input}->read(my $buffer, 12) || croak("Can't get Inno header"); 33 | my $SetupLdrExeHeader = unpackbinary($buffer, '(L3)<', 'ID', 'OffsetTableOffset', 'NotOffsetTableOffset'); 34 | ($SetupLdrExeHeader->{ID} == $SetupLdrExeHeaderID) || croak("Unknown file type"); 35 | ($SetupLdrExeHeader->{OffsetTableOffset} == ~$SetupLdrExeHeader->{NotOffsetTableOffset}) || croak("Offset table pointer checksum error"); 36 | $self->{Input}->seek($SetupLdrExeHeader->{OffsetTableOffset}, Fcntl::SEEK_SET) || croak("Can't seek to offset table"); 37 | $self->{Input}->read($buffer, 12) || croak("Error reading offset table ID"); 38 | $self->{Interpreter} = Setup::Inno::Interpret->new($buffer); 39 | $self->{Input}->read(my $buffer2, $self->{Interpreter}->OffsetTableSize() - 12) || croak("Error reading offset table"); 40 | $self->{OffsetTable} = $self->{Interpreter}->ParseOffsetTable($buffer . $buffer2); 41 | } or do { 42 | my $exe = Win::Exe->new($self->{Input}); 43 | my $OffsetTable = $exe->FindResource('RcData', $SetupLdrOffsetTableResID) || croak("Can't find offset table resource"); 44 | $self->{Interpreter} = Setup::Inno::Interpret->new(substr($OffsetTable, 0, 12)); 45 | $self->{OffsetTable} = $self->{Interpreter}->ParseOffsetTable($OffsetTable); 46 | }; 47 | 48 | #print("Offset0 " . $self->Offset0() . "\n"); 49 | $self->{Input}->seek($self->Offset0(), Fcntl::SEEK_SET) || croak("Can't seek to setup.0 offset"); 50 | $self->{Input}->read(my $buffer, 64) || croak("Error reading setup ID"); 51 | $self->{TestID} = unpack('Z64', $buffer); 52 | $self->{Interpreter}->ReBless($self->{TestID}); 53 | $self->{Filename} = $filename; 54 | 55 | return $self; 56 | } 57 | 58 | sub DiskSpanning { 59 | return shift()->{OffsetTable}->{Offset1} == 0; 60 | } 61 | 62 | sub DiskInfo { 63 | my ($self) = @_; 64 | if ($self->{OffsetTable}->{Offset1} == 0) { 65 | if (!defined($self->{DiskInfo})) { 66 | $self->{DiskInfo} = $self->{Interpreter}->DiskInfo($self->{Filename}, $self->Setup0->{Header}); 67 | } 68 | return $self->{DiskInfo}; 69 | } 70 | return undef; 71 | } 72 | 73 | sub Offset0 { 74 | return shift()->{OffsetTable}->{Offset0}; 75 | } 76 | 77 | sub Offset1 { 78 | return shift()->{OffsetTable}->{Offset1}; 79 | } 80 | 81 | sub TotalSize { 82 | return shift()->{OffsetTable}->{TotalSize}; 83 | } 84 | 85 | sub Version { 86 | my ($self) = @_; 87 | return $self->{Interpreter}->Version; 88 | } 89 | 90 | sub Setup0 { 91 | my ($self) = @_; 92 | if (!$self->{Setup0}) { 93 | $self->{Setup0} = { }; 94 | $self->{Input}->seek($self->Offset0() + 64, Fcntl::SEEK_SET); 95 | my $struct = $self->{Interpreter}->StructReader($self->{Input}); 96 | $self->{Setup0}->{Header} = $self->{Interpreter}->SetupHeader($struct); 97 | $self->{Setup0}->{Languages} = $self->{Interpreter}->SetupLanguages($struct, $self->{Setup0}->{Header}->{NumLanguageEntries}); 98 | $self->{Setup0}->{CustomMessages} = $self->{Interpreter}->SetupCustomMessages($struct, $self->{Setup0}->{Header}->{NumCustomMessageEntries}); 99 | $self->{Setup0}->{Permissions} = $self->{Interpreter}->SetupPermissions($struct, $self->{Setup0}->{Header}->{NumPermissionEntries}); 100 | $self->{Setup0}->{Types} = $self->{Interpreter}->SetupTypes($struct, $self->{Setup0}->{Header}->{NumTypeEntries}); 101 | $self->{Setup0}->{Components} = $self->{Interpreter}->SetupComponents($struct, $self->{Setup0}->{Header}->{NumComponentEntries}); 102 | $self->{Setup0}->{Tasks} = $self->{Interpreter}->SetupTasks($struct, $self->{Setup0}->{Header}->{NumTaskEntries}); 103 | $self->{Setup0}->{Dirs} = $self->{Interpreter}->SetupDirs($struct, $self->{Setup0}->{Header}->{NumDirEntries}); 104 | $self->{Setup0}->{Files} = $self->{Interpreter}->SetupFiles($struct, $self->{Setup0}->{Header}->{NumFileEntries}); 105 | $self->{Setup0}->{Icons} = $self->{Interpreter}->SetupIcons($struct, $self->{Setup0}->{Header}->{NumIconEntries}); 106 | $self->{Setup0}->{IniEntries} = $self->{Interpreter}->SetupIniEntries($struct, $self->{Setup0}->{Header}->{NumIniEntries}); 107 | $self->{Setup0}->{RegistryEntries} = $self->{Interpreter}->SetupRegistryEntries($struct, $self->{Setup0}->{Header}->{NumRegistryEntries}); 108 | $self->{Setup0}->{InstallDelete} = $self->{Interpreter}->SetupDelete($struct, $self->{Setup0}->{Header}->{NumInstallDeleteEntries}); 109 | $self->{Setup0}->{UninstallDelete} = $self->{Interpreter}->SetupDelete($struct, $self->{Setup0}->{Header}->{NumUninstallDeleteEntries}); 110 | $self->{Setup0}->{Run} = $self->{Interpreter}->SetupRun($struct, $self->{Setup0}->{Header}->{NumRunEntries}); 111 | $self->{Setup0}->{UninstallRun} = $self->{Interpreter}->SetupRun($struct, $self->{Setup0}->{Header}->{NumUninstallRunEntries}); 112 | $self->{Setup0}->{Binaries} = $self->{Interpreter}->SetupBinaries($struct, $self->{Interpreter}->Compression1($self->{Setup0}->{Header})); 113 | # Get the current location so we can seek to the locations list later. 114 | # It's stored in its own LZMA stream. 115 | $self->{Setup0}->{OffsetLocations} = $self->{Input}->tell(); 116 | #printf("Locations offset: 0x%08x\n", $self->{Setup0}->{OffsetLocations}); 117 | } 118 | return $self->{Setup0}; 119 | } 120 | 121 | sub FileLocations { 122 | my ($self) = @_; 123 | if (!$self->{FileLocations}) { 124 | my $setup0 = $self->Setup0(); 125 | $self->{Input}->seek($setup0->{OffsetLocations}, Fcntl::SEEK_SET); 126 | my $struct = $self->{Interpreter}->StructReader($self->{Input}); 127 | #while (1) { $self->{Input}->seek(0x01000000, Fcntl::SEEK_SET); } 128 | $self->{FileLocations} = $self->{Interpreter}->SetupFileLocations($struct, $setup0->{Header}->{NumFileLocationEntries}); 129 | } 130 | return $self->{FileLocations}; 131 | } 132 | 133 | sub FileCount { 134 | my ($self) = @_; 135 | my $setup0 = $self->Setup0(); 136 | return scalar(@{$setup0->{Files}}); 137 | } 138 | 139 | sub FileInfo { 140 | my ($self, $index) = @_; 141 | ($index < 0) && croak("Negative file index"); 142 | my $setup0 = $self->Setup0(); 143 | my $locations = $self->FileLocations(); 144 | my $file = $self->{Setup0}->{Files}->[$index]; 145 | my $location = $locations->[$file->{LocationEntry}]; 146 | #return { File => $file, Location => $location }; 147 | my $type; 148 | my $name = $file->{DestName}; 149 | given ($file->{FileType}) { 150 | when (/UserFile/i) { 151 | if ($name =~ /^{code:[a-zA-Z0-9_|]*?}/i) { 152 | $type = 'code'; 153 | } elsif ($name =~ /^{(.*?)}/i) { 154 | $type = $1; 155 | #print("Generic destination type: $type\n"); 156 | } else { 157 | $type = 'unknown'; 158 | print("Unsupported destination: $name\n"); 159 | } 160 | $name =~ s/^{.*?}\\//; 161 | $name =~ s#\\#/#g; 162 | } 163 | when (/UninstExe/i) { 164 | $type = 'uninstexe'; 165 | # TODO: Find out if the name of the uninstaller exe is found somewhere else 166 | if (!$file->{DestName}) { 167 | $name = $DefaultUninstallExeName; 168 | } 169 | } 170 | when (/RegSvrExe/i) { 171 | $type = 'regsvrexe'; 172 | if (!$file->{DestName}) { 173 | $name = $DefaultRegSvrExeName; 174 | } 175 | } 176 | default { 177 | $type = 'unknown'; 178 | print("Unknown file type: $file->{FileType}\n"); 179 | } 180 | } 181 | return { 182 | Size => $location->{OriginalSize}, 183 | Date => $location->{TimeStamp} || $location->{SourceTimeStamp}, 184 | Compressed => $location->{Flags}->{ChunkCompressed} || $location->{Flags}->{foChunkCompressed}, 185 | Encrypted => $location->{Flags}->{ChunkEncrypted} || $location->{Flags}->{foChunkEncrypted}, 186 | Type => $type, 187 | OriginalName => $file->{DestName}, 188 | Name => $name, 189 | }; 190 | } 191 | 192 | sub ReadFile { 193 | my ($self, $index, $password) = @_; 194 | my $setup0 = $self->Setup0(); 195 | my $locations = $self->FileLocations(); 196 | my $file = $self->{Setup0}->{Files}->[$index]; 197 | my $location = $locations->[$file->{LocationEntry}]; 198 | if ($self->DiskSpanning()) { 199 | my @slices; 200 | for my $slice ($location->{FirstSlice}..$location->{LastSlice}) { 201 | push(@slices, $self->DiskInfo()->[$slice]); 202 | } 203 | my $info = $self->DiskInfo()->[$location->{FirstSlice}]; 204 | return $self->{Interpreter}->ReadFile($info->{Input}, $setup0->{Header}, $location, 0, $password, @slices); 205 | } else { 206 | #$self->{Input}->seek($self->Offset1(), Fcntl::SEEK_SET); 207 | return $self->{Interpreter}->ReadFile($self->{Input}, $setup0->{Header}, $location, $self->Offset1(), $password); 208 | } 209 | } 210 | 211 | sub FindFiles { 212 | my ($self, $re) = @_; 213 | my @ret; 214 | for (my $i = 0; $i < $self->FileCount; $i++) { 215 | my $file = $self->FileInfo($i); 216 | if ($file->{Name} =~ $re) { 217 | push(@ret, $i); 218 | } 219 | } 220 | return @ret; 221 | } 222 | 223 | sub VerifyPassword { 224 | my ($self, $password) = @_; 225 | return $self->{Interpreter}->VerifyPassword($self->Setup0(), $password); 226 | } 227 | 228 | 1; 229 | 230 | -------------------------------------------------------------------------------- /Setup/Inno/BlockReader.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::BlockReader; 2 | 3 | use strict; 4 | require 5.006_000; 5 | use Digest; 6 | use Carp; 7 | use Setup::Inno::LzmaReader; 8 | 9 | sub new { 10 | my ($class, $handle, $offset, $blocksize) = @_; 11 | 12 | $handle->seek($offset, Fcntl::SEEK_SET) if defined($offset); 13 | $blocksize = 4096 unless defined $blocksize; 14 | 15 | $handle->read(my $buffer, 9) || croak("Can't read compression header"); 16 | my ($headercrc, $storedsize, $compressed) = unpack('(L2c)<', $buffer); 17 | my $crc = Digest->new('CRC-32'); 18 | # CRC32 digest includes data length value and compressed flag 19 | $crc->add(substr($buffer, 4, 5)); 20 | my $digest = $crc->digest; 21 | ($digest == $headercrc) || croak("Invalid CRC in compression header"); 22 | 23 | my $framesize = $blocksize + 4; 24 | my ($reloffset, $bytes, $compressedsize, $packed) = (0, 0, 0, ''); 25 | do { 26 | $bytes = $framesize < $storedsize - $reloffset ? $framesize : $storedsize - $reloffset; 27 | if ($bytes >= 4) { 28 | $handle->read(my $indata, $bytes); 29 | my $blockcrc = unpack('L<', substr($indata, 0, 4)); 30 | my $data = substr($indata, 4, $bytes - 4); 31 | $crc->new; 32 | $crc->add($data); 33 | if ($crc->digest != $blockcrc) { 34 | croak("Invalid CRC in block"); 35 | } 36 | $packed .= $data; 37 | $reloffset += $bytes; 38 | $compressedsize += $bytes - 4; 39 | } 40 | } while ($bytes > 0 && $reloffset < $storedsize); 41 | 42 | $handle = IO::File->new(\$packed, 'r') || croak("Can't create file handle for packed data: $!"); 43 | 44 | if ($compressed) { 45 | $handle = Setup::Inno::LzmaReader->new($handle, $compressedsize); 46 | } 47 | return $handle; 48 | } 49 | 50 | 1; 51 | -------------------------------------------------------------------------------- /Setup/Inno/FieldReader.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Set; 4 | 5 | use strict; 6 | use overload '""' => \&describe; 7 | 8 | sub new { 9 | my ($class, $data) = @_; 10 | my @self = unpack('C*', $data); 11 | return bless(\@self, $class); 12 | } 13 | 14 | sub get { 15 | my ($self, $bit) = @_; 16 | return ($self->[$bit >> 3] >> ($bit & 0x7)) & 1; 17 | } 18 | 19 | sub set { 20 | my ($self, $bit, $value) = @_; 21 | if ($value) { 22 | $self->[$bit >> 3] |= (1 << ($bit & 0x7)); 23 | } else { 24 | $self->[$bit >> 3] &= ~(1 << ($bit & 0x7)); 25 | } 26 | } 27 | 28 | sub describe { 29 | my ($self) = @_; 30 | return unpack('B*', join('', @{$self})); 31 | } 32 | 33 | sub match { 34 | my ($self, $fields) = @_; 35 | my $ret = { }; 36 | for (my $i = 0; $i < @{$fields}; $i++) { 37 | $ret->{$fields->[$i]} = $self->get($i); 38 | } 39 | return $ret; 40 | } 41 | 42 | package Setup::Inno::FieldReader; 43 | 44 | use strict; 45 | use Encode; 46 | use POSIX qw(ceil); 47 | 48 | sub new { 49 | my ($class, $reader, $debug) = @_; 50 | return bless({ Reader => $reader, Debug => $debug }, $class); 51 | } 52 | 53 | sub reader { 54 | return shift()->{Reader}; 55 | } 56 | 57 | sub read { 58 | return shift()->{Reader}->read(@_); 59 | } 60 | 61 | sub tell { 62 | return shift()->{Reader}->tell(@_); 63 | } 64 | 65 | sub seek { 66 | return shift()->{Reader}->seek(@_); 67 | } 68 | 69 | sub close { 70 | return shift()->{Reader}->close(@_); 71 | } 72 | 73 | # Reads a string, the type is determined by the first argument 74 | # Arguments: 75 | # The type of string 76 | # 0 or undefined: Binary string (no conversion) 77 | # 1: 8-bit string, using Windows codepage 1252 78 | # 2: Unicode string, using UTF-16 79 | # The string length in bytes (only for fixed length strings) 80 | sub ReadString { 81 | my ($self, $coding, $length) = @_; 82 | if (!defined($length)) { 83 | warn("Reading 4 bytes from " . $self->{Reader}->tell) if $self->{Debug}; 84 | # Note that the length is the number of bytes, not characters 85 | $self->{Reader}->read(my $buffer, 4) || die("Can't read string length"); 86 | ($length) = unpack('L<', $buffer); 87 | } 88 | warn("Reading $length bytes from " . $self->{Reader}->tell) if $self->{Debug}; 89 | ($self->{Reader}->read(my $buffer, $length) == $length) || die("Can't read string"); 90 | if ($coding) { 91 | if ($coding == 1) { 92 | return decode('cp1252', $buffer); 93 | } elsif ($coding == 2) { 94 | return decode('UTF-16LE', $buffer); 95 | } 96 | } else { 97 | return $buffer; 98 | } 99 | } 100 | 101 | # Reads a CP-1252 (Windows Latin) string 102 | # Arguments: 103 | # The string length (if fixed size, optional) 104 | sub ReadAnsiString { 105 | my ($self, $length) = @_; 106 | return $self->ReadString(1, $length); 107 | } 108 | 109 | # Reads a UTF-16 string (actually UCS-2, but that was a bad design decision on Microsoft's side) 110 | # Arguments: 111 | # The string length (if fixed size, optional) 112 | sub ReadWideString { 113 | my ($self, $length) = @_; 114 | return $self->ReadString(2, $length); 115 | } 116 | 117 | # Reads a Delphi set of ... 118 | # Sets are represented in Delphi as bit fields, with the highest possible 119 | # value determining the number of bits required. 120 | # Arguments: 121 | # The number of choices (bits) in the set 122 | # or 123 | # An arrayref of field names 124 | # Return: 125 | # Set object, supporting get(bit #) and set(bit #, value) methods 126 | # or (if fields names given) 127 | # A hashref containing => pairs 128 | # Example: 129 | # Bitfield : set of Char; 130 | # The maximum value for Char is 255, so 256 bits are required, corresponding to 32 bytes. 131 | sub ReadSet { 132 | my ($self, $fields) = @_; 133 | my $bits; 134 | if (ref($fields) eq 'ARRAY') { 135 | $bits = @{$fields}; 136 | } else { 137 | $bits = $fields; 138 | } 139 | my $bytes = ceil($bits / 8); 140 | warn("Reading $bytes bytes") if $self->{Debug}; 141 | $self->{Reader}->read(my $buffer, $bytes) || die("Can't read set"); 142 | my $set = Setup::Inno::Set->new($buffer); 143 | if (ref($fields) eq 'ARRAY') { 144 | return $set->match($fields); 145 | } else { 146 | return $set; 147 | } 148 | } 149 | 150 | # Reads a Delphi Enum 151 | # The storage required for Enums depends on the number of enumeration values. 152 | # Arguments: 153 | # The number of enumerated values (choices) 154 | # or 155 | # An arrayref of enumerated names 156 | # Return: 157 | # The enumerated value 158 | # or (if names were given) 159 | # The enumerated name 160 | sub ReadEnum { 161 | my ($self, $names) = @_; 162 | my $values; 163 | if (ref($names) eq 'ARRAY') { 164 | $values = @{$names}; 165 | } else { 166 | $values = $names; 167 | } 168 | my $value; 169 | if ($values - 1 <= 255) { 170 | warn("Reading 1 byte") if $self->{Debug}; 171 | $value = $self->ReadByte(); 172 | } elsif ($values - 1 <= 65535) { 173 | warn("Reading 2 bytes") if $self->{Debug}; 174 | $value = $self->ReadWord(); 175 | } elsif ($values - 1 <= 4294967295) { 176 | warn("Reading 4 bytes") if $self->{Debug}; 177 | $value = $self->ReadLongWord(); 178 | } else { 179 | warn("Reading 8 bytes") if $self->{Debug}; 180 | $value = $self->ReadInt64(); 181 | } 182 | if (ref($names) eq 'ARRAY') { 183 | return $names->[$value]; 184 | } else { 185 | return $value; 186 | } 187 | } 188 | 189 | =comment 190 | Type Storage size Range 191 | 192 | Byte 1 0 to 255 193 | ShortInt 1 -127 to 127 194 | Word 2 0 to 65,535 195 | SmallInt 2 -32,768 to 32,767 196 | LongWord 4 0 to 4,294,967,295 197 | Cardinal 4* 0 to 4,294,967,295 198 | LongInt 4 -2,147,483,648 to 2,147,483,647 199 | Integer 4* -2,147,483,648 to 2,147,483,647 200 | Int64 8 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 201 | 202 | Single 4 7 significant digits, exponent -38 to +38 203 | Currency 8 50+ significant digits, fixed 4 decimal places 204 | Double 8 15 significant digits, exponent -308 to +308 205 | Extended 10 19 significant digits, exponent -4932 to +4932 206 | 207 | * Note : the Integer and Cardinal types are both 4 bytes in size at present (Delphi release 7), but are not guaranteed to be this size in the future. All other type sizes are guaranteed. 208 | =cut 209 | 210 | sub ReadByteArray { 211 | my ($self, $length) = @_; 212 | warn("Reading $length bytes") if $self->{Debug}; 213 | $self->{Reader}->read(my $buffer, $length) || die("Can't read byte array"); 214 | return $buffer; 215 | } 216 | 217 | sub ReadShortInt { 218 | my ($self) = @_; 219 | warn("Reading 1 byte") if $self->{Debug}; 220 | $self->{Reader}->read(my $buffer, 1) || die("Can't read byte"); 221 | return unpack('c', $buffer); 222 | } 223 | 224 | sub ReadByte { 225 | my ($self) = @_; 226 | warn("Reading 1 byte") if $self->{Debug}; 227 | $self->{Reader}->read(my $buffer, 1) || die("Can't read byte"); 228 | return unpack('C', $buffer); 229 | } 230 | 231 | sub ReadSmallInt { 232 | my ($self) = @_; 233 | warn("Reading 2 bytes") if $self->{Debug}; 234 | $self->{Reader}->read(my $buffer, 2) || die("Can't read word"); 235 | return unpack('s<', $buffer); 236 | } 237 | 238 | sub ReadWord { 239 | my ($self) = @_; 240 | warn("Reading 2 bytes") if $self->{Debug}; 241 | $self->{Reader}->read(my $buffer, 2) || die("Can't read word"); 242 | return unpack('S<', $buffer); 243 | } 244 | 245 | sub ReadLongInt { 246 | my ($self) = @_; 247 | warn("Reading 4 bytes") if $self->{Debug}; 248 | $self->{Reader}->read(my $buffer, 4) || die("Can't read longword"); 249 | return unpack('l<', $buffer); 250 | } 251 | 252 | sub ReadLongWord { 253 | my ($self) = @_; 254 | warn("Reading 4 bytes") if $self->{Debug}; 255 | $self->{Reader}->read(my $buffer, 4) || die("Can't read longword"); 256 | return unpack('L<', $buffer); 257 | } 258 | 259 | sub ReadSingle { 260 | my ($self) = @_; 261 | warn("Reading 4 bytes") if $self->{Debug}; 262 | $self->{Reader}->read(my $buffer, 4) || die("Can't read float"); 263 | return unpack('f<', $buffer); 264 | } 265 | 266 | sub ReadDouble { 267 | my ($self) = @_; 268 | warn("Reading 8 bytes") if $self->{Debug}; 269 | $self->{Reader}->read(my $buffer, 8) || die("Can't read double"); 270 | return unpack('d<', $buffer); 271 | } 272 | 273 | sub ReadInt64 { 274 | my ($self) = @_; 275 | warn("Reading 8 bytes") if $self->{Debug}; 276 | $self->{Reader}->read(my $buffer, 8) || die("Can't read quadword"); 277 | return unpack('q<', $buffer); 278 | } 279 | 280 | *ReadBoolean = \&ReadByte; 281 | *ReadCardinal = \&ReadLongWord; 282 | *ReadInteger = \&ReadLongInt; 283 | *ReadInteger64 = \&ReadInt64; 284 | 285 | 1; 286 | 287 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret; 4 | 5 | use strict; 6 | use Switch 'Perl6'; 7 | use Carp; 8 | use Setup::Inno::FieldReader; 9 | 10 | our $SetupLdrOffsetTableID = { 11 | # These are byte strings 12 | '2008' => "rDlPtS02\x{87}\x{65}\x{56}\x{78}", # 2.0.8 until 3.0.8 13 | '4000' => "rDlPtS04\x{87}\x{65}\x{56}\x{78}", # 4.0.0 until 4.0.2 14 | '4003' => "rDlPtS05\x{87}\x{65}\x{56}\x{78}", # 4.0.3 until 4.0.9 15 | '4010' => "rDlPtS06\x{87}\x{65}\x{56}\x{78}", # 4.0.10 until 4.1.5 16 | '4106' => "rDlPtS07\x{87}\x{65}\x{56}\x{78}", # 4.1.6 until 5.1.2 17 | '5105' => "rDlPtS\x{cd}\x{e6}\x{d7}\x{7b}\x{0b}\x{2a}", # from 5.1.5 18 | }; 19 | our $InterpreterVersions = [ 20 | 2008, 21 | 4000, 22 | 4003, 23 | 4010, 24 | 4106, 25 | 5105, 26 | 5309, 27 | ]; 28 | 29 | sub new { 30 | my ($class, $setupid) = @_; 31 | given ($setupid) { 32 | when ($SetupLdrOffsetTableID->{'2008'}) { 33 | require 'Setup/Inno/Interpret2008.pm'; 34 | return bless({ Version => '2008' }, 'Setup::Inno::Interpret2008'); 35 | } 36 | when ($SetupLdrOffsetTableID->{'4000'}) { 37 | require 'Setup/Inno/Interpret4000.pm'; 38 | return bless({ Version => '4000' }, 'Setup::Inno::Interpret4000'); 39 | } 40 | when ($SetupLdrOffsetTableID->{'4003'}) { 41 | require 'Setup/Inno/Interpret4003.pm'; 42 | return bless({ Version => '4003' }, 'Setup::Inno::Interpret4003'); 43 | } 44 | when ($SetupLdrOffsetTableID->{'4010'}) { 45 | require 'Setup/Inno/Interpret4010.pm'; 46 | return bless({ Version => '4010' }, 'Setup::Inno::Interpret4010'); 47 | } 48 | when ($SetupLdrOffsetTableID->{'4106'}) { 49 | require 'Setup/Inno/Interpret4106.pm'; 50 | return bless({ Version => '4106' }, 'Setup::Inno::Interpret4106'); 51 | } 52 | when ($SetupLdrOffsetTableID->{'5105'}) { 53 | require 'Setup/Inno/Interpret5105.pm'; 54 | return bless({ Version => '5105' }, 'Setup::Inno::Interpret5105'); 55 | } 56 | } 57 | croak("Unknown offset table format, not Inno Setup?"); 58 | } 59 | 60 | # Returns the InnoSetup version this interpreter instance can handle. 61 | # The version is only precise if ReBless has been called. 62 | # Returns just the setup header version otherwise. 63 | # Override this method if your class name does not follow the usual scheme 64 | sub Version { 65 | return shift->{Version}; 66 | #my ($self) = @_; 67 | #if (ref($self) =~ /^Setup::Inno::Interpret([0-9]{4}u?)$/) { 68 | # return $1; 69 | #} 70 | #return '0000'; 71 | } 72 | 73 | # Check a decompressed file's checksum. 74 | # The default implementation returns 1, for formats without file checksum. 75 | # Implement the correct checksum algorithm for each version. 76 | # Arguments: 77 | # A string containing the file's data 78 | # The file location entry 79 | sub CheckFile { 80 | return 1; 81 | } 82 | 83 | sub OffsetTableSize { 84 | # All offset tables contain at least the 12 byte ID, override if you need more 85 | return 12; 86 | } 87 | 88 | sub ParseOffsetTable { 89 | # No-op 90 | } 91 | 92 | # Compression used for files 93 | # Returns undef if no compression is used/supported ('Stored' type) 94 | # Arguments: 95 | # The setup.0 header 96 | sub Compression1 { return undef; } 97 | 98 | # Deoptimize executables (the transformation helps with compression) 99 | # Arguments: 100 | # The optimized executable data 101 | # The address offset (optional, 0 assumed) 102 | sub TransformCallInstructions { 103 | my ($self, $data, $offset) = @_; 104 | return $data; 105 | } 106 | 107 | # Change the blessing of $self to the closest available package according to a version string. 108 | # In other words, parse the version string and sweep over the available version-specific interpreter 109 | # classes until one is found that can handle data for this InnoSetup version. 110 | # Also stores the exact version, so an appropriate structure reader can be created later, 111 | # Arguments: 112 | # The InnoSetup version string 113 | sub ReBless { 114 | my ($self, $verstr) = @_; 115 | if ($verstr =~ /\(([0-9])\.([0-9])\.([0-9])([0-9])?([a-z])?\)(\s*\(([a-z])?\))?/) { 116 | my $bareversion = "$1$2"; 117 | $bareversion .= defined($4) ? "$3$4" : "0$3"; 118 | $self->{IsUnicode} = ((defined($5) && $5 eq 'u') || (defined($7) && $7 eq 'u') ? 'u' : ''); 119 | my $version = $bareversion . $self->{IsUnicode}; 120 | $self->{Version} = $version; 121 | my ($low, $high) = (0, $#{$InterpreterVersions}); 122 | my $found; 123 | if ($bareversion >= $InterpreterVersions->[$high]) { 124 | # above or equal max 125 | $found = $InterpreterVersions->[$high]; 126 | } elsif ($bareversion < $InterpreterVersions->[$low]) { 127 | # below min 128 | $high = $low; 129 | } 130 | while (!defined($found) && $low < $high) { 131 | my $mid = int(($low + $high) / 2); 132 | if ($bareversion >= $InterpreterVersions->[$mid]) { 133 | # above or equal mid (and below high) 134 | if ($bareversion < $InterpreterVersions->[$mid + 1]) { 135 | $found = $InterpreterVersions->[$mid]; 136 | } else { 137 | $low = $mid; 138 | } 139 | } else { 140 | # below mid (and above or equal low) 141 | $high = $mid; 142 | } 143 | } 144 | if (defined($found)) { 145 | require "Setup/Inno/Interpret$found.pm"; 146 | my $class = "Setup::Inno::Interpret$found"; 147 | bless($self, $class); 148 | } else { 149 | die("Internal error: Interpreter class not found for $bareversion"); 150 | } 151 | } elsif ($verstr =~ /My Inno Setup Extensions Setup Data \(3.0.6.[12]\)/) { 152 | # Martijn Laan's extensions, unsupported for now 153 | # Bless/return '3008' here and implement Interpret30008.pm once you support them 154 | croak("Unsupported version: $verstr"); 155 | } else { 156 | croak("Unsupported version: $verstr"); 157 | } 158 | } 159 | 160 | # Create a structure reader for setup.0 data. 161 | # You should only call this method if you have assigned the exact version with ReBless(). 162 | # Uses FieldReader() to construct a suitable reader (handling decompression etc.). 163 | # Arguments: 164 | # A file handle pointing to setup.0 data 165 | sub StructReader { 166 | my ($self, $reader) = @_; 167 | my $version = $self->{Version}; 168 | require "Setup/Inno/Struct$version.pm"; 169 | my $class = "Setup::Inno::Struct$version"; 170 | return $class->new($self->FieldReader($reader)); 171 | } 172 | 173 | # Create a field reader from a file handle 174 | # The default implementation does not use compression and reads Delphi objects 175 | # as they are stored in memory. 176 | # Arguments: 177 | # A file handle serving data to the reader 178 | # An offset from the start of the file, or undef if no seeking is necessary 179 | sub FieldReader { 180 | my ($self, $input) = @_; 181 | return Setup::Inno::FieldReader->new($input); 182 | } 183 | 184 | # Fetch information about setup.bin files, for multi-disk installers 185 | # Arguments: 186 | # The filename of the setup executable 187 | # The Setup0 header 188 | sub DiskInfo { return undef; } 189 | 190 | # Read a file 191 | # The default implementation returns undef 192 | # Arguments: 193 | # A file handle serving setup.1 data 194 | # The setup.0 header 195 | # The file location data 196 | # The start offset of setup.1 data 197 | # The decryption password (ignored if not encrypted) 198 | # An list of slice records occupied by this file (including the first, optional) 199 | sub ReadFile { return undef; } 200 | 201 | # Read the setup BMPs and compression DLL 202 | # Not supported before version 4000 203 | # Arguments: 204 | # A FieldReader 205 | # A boolean specifying if the compression DLL should be read (use $struct->Compression1($header) to check) 206 | sub SetupBinaries { return undef; } 207 | 208 | # Read the various setup.0 data blocks 209 | # Arguments: 210 | # A structure reader suitable for the setup.0 version 211 | # The number of records to read (except for SetupHeader, it has only one) 212 | sub SetupHeader { 213 | my ($self, $struct) = @_; 214 | return $struct->TSetupHeader(); 215 | } 216 | 217 | sub SetupLanguages { 218 | my ($self, $struct, $count) = @_; 219 | return [ map { $struct->TSetupLanguageEntry() } 0..($count - 1) ]; 220 | } 221 | 222 | sub SetupCustomMessages { 223 | my ($self, $struct, $count) = @_; 224 | return [ map { $struct->TSetupCustomMessageEntry() } (0..$count - 1) ]; 225 | } 226 | 227 | sub SetupPermissions { 228 | my ($self, $struct, $count) = @_; 229 | return [ map { $struct->TSetupPermissionEntry() } (0..$count - 1) ]; 230 | } 231 | 232 | sub SetupTypes { 233 | my ($self, $struct, $count) = @_; 234 | return [ map { $struct->TSetupTypeEntry() } (0..$count - 1) ]; 235 | } 236 | 237 | sub SetupComponents { 238 | my ($self, $struct, $count) = @_; 239 | return [ map { $struct->TSetupComponentEntry() } (0..$count - 1) ]; 240 | } 241 | 242 | sub SetupTasks { 243 | my ($self, $struct, $count) = @_; 244 | return [ map { $struct->TSetupTaskEntry() } (0..$count - 1) ]; 245 | } 246 | 247 | sub SetupDirs { 248 | my ($self, $struct, $count) = @_; 249 | return [ map { $struct->TSetupDirEntry() } (0..$count - 1) ]; 250 | } 251 | 252 | sub SetupFiles { 253 | my ($self, $struct, $count) = @_; 254 | return [ map { $struct->TSetupFileEntry() } (0..$count - 1) ]; 255 | } 256 | 257 | sub SetupIcons { 258 | my ($self, $struct, $count) = @_; 259 | return [ map { $struct->TSetupIconEntry() } (0..$count - 1) ]; 260 | } 261 | 262 | sub SetupIniEntries { 263 | my ($self, $struct, $count) = @_; 264 | return [ map { $struct->TSetupIniEntry() } (0..$count - 1) ]; 265 | } 266 | 267 | sub SetupRegistryEntries { 268 | my ($self, $struct, $count) = @_; 269 | return [ map { $struct->TSetupRegistryEntry() } (0..$count - 1) ]; 270 | } 271 | 272 | sub SetupDelete { 273 | my ($self, $struct, $count) = @_; 274 | return [ map { $struct->TSetupDeleteEntry() } (0..$count - 1) ]; 275 | } 276 | 277 | sub SetupRun { 278 | my ($self, $struct, $count) = @_; 279 | return [ map { $struct->TSetupRunEntry() } (0..$count - 1) ]; 280 | } 281 | 282 | sub SetupFileLocations { 283 | my ($self, $struct, $count) = @_; 284 | return [ map { 285 | my $entry = $struct->TSetupFileLocationEntry(); 286 | # StartOffset should actually be an unsigned type - we can't address full 4GB files like this. 287 | # This has worked in InnoSetup because they implement their Seek function with a Cardinal offset (which is unsigned). 288 | # The compiler should report the signed/unsigned mismatch instead of typecasting, but it looks like it's being ignored. 289 | $entry->{StartOffset} = unpack('L', pack('l', $entry->{StartOffset})); 290 | $entry; 291 | } (0..$count - 1) ]; 292 | } 293 | 294 | # Verifies that a supplied password is correct 295 | # Arguments: 296 | # The Setup0 header 297 | # The password 298 | sub VerifyPassword { 299 | return 1; 300 | } 301 | 302 | 1; 303 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret2008.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret2008; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret); 7 | use Switch 'Perl6'; 8 | use Fcntl; 9 | use Digest; 10 | use Encode; 11 | use IO::Uncompress::AnyInflate; 12 | use IO::Uncompress::Bunzip2; 13 | use Win::Exe::Util; 14 | 15 | sub OffsetTableSize { 16 | return 44; 17 | } 18 | 19 | sub ParseOffsetTable { 20 | my ($self, $data) = @_; 21 | (length($data) >= $self->OffsetTableSize()) || die("Invalid offset table size"); 22 | my $ofstable = unpackbinary($data, '(a12L8)<', 'ID', 'TotalSize', 'OffsetEXE', 'CompressedSizeEXE', 'UncompressedSizeEXE', 'AdlerEXE', 'OffsetMsg', 'Offset0', 'Offset1'); 23 | return $ofstable; 24 | } 25 | 26 | sub CheckFile { 27 | my ($self, $data, $location) = @_; 28 | my $digest = Digest->new('Adler-32'); 29 | $digest->add($data); 30 | return $digest->digest() eq $location->{Checksum}; 31 | } 32 | 33 | sub Compression1 { 34 | my ($self, $header) = @_; 35 | if (!defined($header->{CompressMethod}) || $header->{CompressMethod} =~ /Stored/i || $header->{CompressMethod} eq 0) { 36 | return undef; 37 | } 38 | return $header->{CompressMethod}; 39 | } 40 | 41 | sub FieldReader { 42 | my ($self, $reader, $offset) = @_; 43 | if (defined($offset)) { 44 | $reader->seek($offset, Fcntl::SEEK_SET); 45 | } 46 | my $creader = IO::Uncompress::AnyInflate->new($reader, Transparent => 0) || die("Can't create zlib decompressor"); 47 | my $freader = Setup::Inno::FieldReader->new($creader) || die("Can't create field reader"); 48 | return $freader; 49 | } 50 | 51 | sub ReadFile { 52 | my ($self, $input, $header, $location, $offset1, $password) = @_; 53 | 54 | $input->seek($offset1 + $location->{StartOffset}, Fcntl::SEEK_SET); 55 | 56 | my $reader; 57 | if ($location->{Flags}->{ChunkCompressed}) { 58 | given ($header->{CompressMethod}) { 59 | when ('Zip') { 60 | $reader = IO::Uncompress::AnyInflate->new($input, Transparent => 0) || die("Can't create zlib reader"); 61 | } 62 | when ('Bzip') { 63 | $reader = IO::Uncompress::Bunzip2->new($input, Transparent => 0) || die("Can't create bzip2 reader"); 64 | } 65 | default { 66 | # Plain reader for stored mode 67 | $reader = $input; 68 | } 69 | } 70 | } else { 71 | $reader = $input; 72 | } 73 | 74 | ($reader->read(my $buffer, $location->{OriginalSize}) >= $location->{OriginalSize}) || die("Can't uncompress file"); 75 | 76 | ($self->CheckFile($buffer, $location)) || die("Invalid file checksum"); 77 | 78 | return $buffer; 79 | } 80 | 81 | sub VerifyPassword { 82 | my ($self, $setup0, $password) = @_; 83 | if ($setup0->{Header}->{Options}->{shPassword}) { 84 | if (!defined($password)) { 85 | return 0 86 | } 87 | my $digest = Digest->new('CRC-32'); 88 | $digest->add(encode('cp1252', $password)); 89 | return $digest->digest() == $setup0->{Header}->{Password}; 90 | } else { 91 | return !defined($password); 92 | } 93 | } 94 | 95 | 1; 96 | 97 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret4000.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret4000; 4 | 5 | use strict; 6 | require 5.006_000; 7 | use base qw(Setup::Inno::Interpret2008); 8 | use Switch 'Perl6'; 9 | use Carp; 10 | use Fcntl; 11 | use Digest; 12 | use Encode; 13 | use IO::File; 14 | use IO::Uncompress::AnyInflate; 15 | use IO::Uncompress::Bunzip2; 16 | use File::Basename; 17 | use Setup::Inno::LzmaReader; 18 | use Crypt::RC4; 19 | 20 | our $ZLIBID = "zlb\x{1a}"; 21 | our $DISKSLICEID = "idska32\x{1a}"; 22 | 23 | sub CheckFile { 24 | my ($self, $data, $location) = @_; 25 | if (defined($location->{Checksum})) { 26 | my $digest = Digest->new('CRC-32'); 27 | $digest->add($data); 28 | # CRC-32 produces a numeric result 29 | return $digest->digest() == $location->{Checksum}; 30 | } 31 | return 1; 32 | } 33 | 34 | sub SetupBinaries { 35 | my ($self, $reader, $compression) = @_; 36 | my $ret = { }; 37 | my $wzimglength = $reader->ReadLongWord(); 38 | $ret->{WizardImage} = $reader->ReadByteArray($wzimglength); 39 | my $wzsimglength = $reader->ReadLongWord(); 40 | $ret->{WizardSmallImage} = $reader->ReadByteArray($wzsimglength); 41 | if ($compression && $compression !~ /LZMA/i) { 42 | my $cmpimglength = $reader->ReadLongWord(); 43 | $ret->{CompressImage} = $reader->ReadByteArray($cmpimglength); 44 | } 45 | return $ret; 46 | } 47 | 48 | # Disk slice handling was new in 4.0.x, the exact version is unknown, but we support it from 4.0.0 49 | sub DiskInfo { 50 | my ($self, $setup, $header) = @_; 51 | my ($basename, $basedir, $suffix) = fileparse($setup, '.exe'); 52 | opendir(my $dir, $basedir); 53 | my @unsorted = grep(/^\Q$basename\E-[0-9]+\.bin$/, readdir($dir)); 54 | closedir($dir); 55 | if (@unsorted < 1) { 56 | croak("Can't find any matching disk files for multi-disk archive"); 57 | } 58 | my @bins = map({ $basedir . $_ } sort({ $a =~ /-([0-9]+)\.bin$/; my $first = $1; $b =~ /-([0-9]+)\.bin$/; my $second = $1; $first cmp $second; } @unsorted)); 59 | my @ret = (); 60 | my $start = 0; 61 | my $disk = 0; 62 | for my $bin (@bins) { 63 | my $input = IO::File->new($bin, 'r') || croak("Can't open $bin: $!"); 64 | my $sliceoffset = 0; 65 | my $dataoffset = 0; 66 | for my $slice (0..($header->{SlicesPerDisk} - 1)) { 67 | $input->read(my $buffer, 12); 68 | my ($sliceid, $size) = unpack('a8L<', $buffer); 69 | $dataoffset += 12; 70 | if ($sliceid eq $DISKSLICEID) { 71 | my $record = { 72 | Input => $input, 73 | File => $bin, 74 | Disk => $disk, 75 | Start => $start, 76 | Size => $size, 77 | SliceOffset => $sliceoffset, 78 | DataOffset => $dataoffset, 79 | }; 80 | push(@ret, $record); 81 | $start += $size; 82 | $dataoffset += $size; 83 | $sliceoffset += $dataoffset; 84 | $input->seek($size, Fcntl::SEEK_CUR); 85 | } else { 86 | carp("$bin has an invalid disk slice header"); 87 | } 88 | } 89 | $disk++; 90 | } 91 | return \@ret; 92 | } 93 | 94 | # IS 4.0.0 might still be using 2.0.8 semantics, needs verification 95 | sub ReadFile { 96 | my ($self, $input, $header, $location, $offset1, $password, @slices) = @_; 97 | 98 | # Check if we have a cached chunk and verify it is the same chunk 99 | # and that we can reach our data with only forward seeking 100 | if ( 101 | defined($self->{ChunkState}) && 102 | $self->{ChunkState}->{FirstSlice} == $location->{FirstSlice} && 103 | $self->{ChunkState}->{LastSlice} == $location->{LastSlice} && 104 | $self->{ChunkState}->{StartOffset} == $location->{StartOffset} && 105 | $self->{ChunkState}->{ChunkCompressedSize} == $location->{ChunkCompressedSize} && 106 | $self->{ChunkState}->{ChunkSuboffset} <= $location->{ChunkSuboffset} 107 | ) { 108 | # Yes, use the cached reader 109 | } else { 110 | # No, create a new reader 111 | 112 | my $encrypted = 0; 113 | if ($location->{Flags}->{ChunkEncrypted} || $location->{Flags}->{foChunkEncrypted}) { 114 | !defined($password) && croak("File is encrypted, but no password was given"); 115 | $encrypted = 1; 116 | } 117 | 118 | my $buffer; 119 | 120 | if (@slices > 1) { 121 | my $i = 0; 122 | my $size = $location->{ChunkCompressedSize} + 4; 123 | if ($encrypted) { 124 | # 8 is the salt length 125 | $size += 8; 126 | } 127 | my $offset = $offset1 + $location->{StartOffset} - $slices[$i]->{SliceOffset}; 128 | my $available = $slices[$i]->{Size} - $offset; 129 | my $slicesize = $available < $size ? $available : $size; 130 | $slices[$i]->{Input}->seek($offset, Fcntl::SEEK_SET) || croak("Can't seek to slice offset: $!"); 131 | $slices[$i]->{Input}->read($buffer, $slicesize) || croak("Can't read slice: $!"); 132 | my $slicedata = $buffer; 133 | $size -= $slicesize; 134 | $i++; 135 | while ($i < @slices && $size > 0) { 136 | $offset = $slices[$i]->{DataOffset}; 137 | $available = $slices[$i]->{Size}; 138 | $slicesize = $available < $size ? $available : $size; 139 | $slices[$i]->{Input}->seek($offset, Fcntl::SEEK_SET) || croak("Can't seek to slice offset: $!"); 140 | $slices[$i]->{Input}->read($buffer, $slicesize) || croak("Can't read slice: $!"); 141 | $slicedata .= $buffer; 142 | $size -= $slicesize; 143 | $i++; 144 | } 145 | # Replace input handle with virtual handle over concatenated data 146 | # This requires Perl 5.6 or later, use IO::String or IO::Scalar for earlier versions 147 | $input = IO::File->new(\$slicedata, 'r') || croak("Can't create file handle for preprocessed data: $!"); 148 | } else { 149 | $input->seek($offset1 + $location->{StartOffset}, Fcntl::SEEK_SET); 150 | } 151 | 152 | $input->read($buffer, 4) || croak("Can't read compressed block magic: $!"); 153 | ($buffer eq $ZLIBID) || croak("Compressed block ID invalid"); 154 | if ($encrypted) { 155 | # encrypted chunks start with a salt 156 | $input->read(my $salt, 8) || croak("Can't read encrypted salt: $!"); 157 | # combined with the password, this gives us the cipher key 158 | my $digest = Digest->new('SHA-1'); 159 | $digest->add($salt); 160 | if ($self->{IsUnicode}) { 161 | $digest->add(encode('UTF-16LE', $password)); 162 | } else { 163 | $digest->add(encode('cp1252', $password)); 164 | } 165 | my $key = $digest->digest(); 166 | my $rc4 = Crypt::RC4->new($key); 167 | # TODO this is kinda redundant... and a waste of heap space. 168 | $input->read(my $encrypted, $location->{ChunkCompressedSize}) || croak("Can't read encrypted data: $!"); 169 | my $decrypted = $rc4->RC4($encrypted); 170 | $input = IO::File->new(\$decrypted, 'r') || croak("Can't create file handle for decrypted data: $!"); 171 | } 172 | 173 | my $reader; 174 | if ($location->{Flags}->{ChunkCompressed} || $location->{Flags}->{foChunkCompressed}) { 175 | given ($self->Compression1($header)) { 176 | when (/Zip$/i) { 177 | $reader = IO::Uncompress::AnyInflate->new($input, Transparent => 0) || croak("Can't create zlib reader: $!"); 178 | } 179 | when (/Bzip$/i) { 180 | $reader = IO::Uncompress::Bunzip2->new($input, Transparent => 0) || croak("Can't create bzip2 reader: $!"); 181 | } 182 | when (/Lzma$/i) { 183 | $reader = Setup::Inno::LzmaReader->new($input, $location->{ChunkCompressedSize}) || croak("Can't create lzma reader: $!"); 184 | } 185 | when (/Lzma2$/i) { 186 | $reader = Setup::Inno::Lzma2Reader->new($input, $location->{ChunkCompressedSize}) || croak("Can't create lzma2 reader: $!"); 187 | } 188 | default { 189 | # Plain reader for stored mode 190 | $reader = $input; 191 | } 192 | } 193 | } else { 194 | $reader = $input; 195 | } 196 | 197 | # Update the reader state 198 | $self->{ChunkState} = { 199 | FirstSlice => $location->{FirstSlice}, 200 | LastSlice => $location->{LastSlice}, 201 | StartOffset => $location->{StartOffset}, 202 | ChunkCompressedSize => $location->{ChunkCompressedSize}, 203 | ChunkSuboffset => 0, 204 | Reader => $reader, 205 | }; 206 | } 207 | 208 | $self->{ChunkState}->{Reader}->seek($location->{ChunkSuboffset} - $self->{ChunkState}->{ChunkSuboffset}, Fcntl::SEEK_CUR); 209 | ($self->{ChunkState}->{Reader}->read(my $buffer, $location->{OriginalSize}) >= $location->{OriginalSize}) || croak("Can't uncompress file: $!"); 210 | 211 | # Update the offset 212 | $self->{ChunkState}->{ChunkSuboffset} = $location->{ChunkSuboffset} + $location->{OriginalSize}; 213 | 214 | if ($location->{Flags}->{CallInstructionOptimized} || $location->{Flags}->{foCallInstructionOptimized}) { 215 | # We could just transform the whole data, but this will expose a flaw in the original algorithm: 216 | # It doesn't detect jump instructions across block boundaries. 217 | # This means we need to process block by block like the original. 218 | # The block size is 64KB here. 219 | for (my $offset = 0; $offset < length($buffer); $offset += 0x10000) { 220 | substr($buffer, $offset, 0x10000) = $self->TransformCallInstructions(substr($buffer, $offset, 0x10000), $offset); 221 | } 222 | } 223 | 224 | ($self->CheckFile($buffer, $location)) || croak("Invalid file checksum"); 225 | 226 | return $buffer; 227 | } 228 | 229 | 1; 230 | 231 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret4003.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret4003; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret4000); 7 | use Digest; 8 | use Win::Exe::Util; 9 | 10 | sub OffsetTableSize { 11 | return 40; 12 | } 13 | 14 | sub ParseOffsetTable { 15 | my ($self, $data) = @_; 16 | (length($data) >= $self->OffsetTableSize()) || die("Invalid offset table size"); 17 | my $ofstable = unpackbinary($data, '(a12L7)<', 'ID', 'TotalSize', 'OffsetEXE', 'CompressedSizeEXE', 'UncompressedSizeEXE', 'CRCEXE', 'Offset0', 'Offset1'); 18 | return $ofstable; 19 | } 20 | 21 | 1; 22 | 23 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret4010.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret4010; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret4003); 7 | 8 | sub OffsetTableSize { 9 | return 44; 10 | } 11 | 12 | sub ParseOffsetTable { 13 | my ($self, $data) = @_; 14 | (length($data) >= $self->OffsetTableSize()) || die("Invalid offset table size"); 15 | my $ofstable = unpackbinary($data, '(a12L8)<', 'ID', 'TotalSize', 'OffsetEXE', 'CompressedSizeEXE', 'UncompressedSizeEXE', 'CRCEXE', 'Offset0', 'Offset1', 'TableCRC'); 16 | my $digest = Digest->new('CRC-32'); 17 | $digest->add(substr($data, 0, 40)); 18 | ($digest->digest() == $ofstable->{TableCRC}) || die("Checksum error"); 19 | return $ofstable; 20 | } 21 | 22 | 1; 23 | 24 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret4106.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret4106; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret4010); 7 | use Fcntl; 8 | use Encode; 9 | use Setup::Inno::BlockReader; 10 | use Setup::Inno::FieldReader; 11 | 12 | sub OffsetTableSize { 13 | return 40; 14 | } 15 | 16 | sub ParseOffsetTable { 17 | my ($self, $data) = @_; 18 | (length($data) >= $self->OffsetTableSize()) || die("Invalid offset table size"); 19 | my $ofstable = unpackbinary($data, '(a12L7)<', 'ID', 'TotalSize', 'OffsetEXE', 'UncompressedSizeEXE', 'CRCEXE', 'Offset0', 'Offset1', 'TableCRC'); 20 | my $digest = Digest->new('CRC-32'); 21 | $digest->add(substr($data, 0, 40)); 22 | ($digest->digest() == $ofstable->{TableCRC}) || die("Checksum error"); 23 | return $ofstable; 24 | } 25 | 26 | sub FieldReader { 27 | my ($self, $reader, $offset) = @_; 28 | if (defined($offset)) { 29 | $reader->seek($offset, Fcntl::SEEK_SET); 30 | } 31 | my $creader = Setup::Inno::BlockReader->new($reader, $offset, 4096) || die("Can't create block reader"); 32 | my $freader = Setup::Inno::FieldReader->new($creader) || die("Can't create field reader"); 33 | return $freader; 34 | } 35 | 36 | sub VerifyPassword { 37 | my ($self, $setup0, $password) = @_; 38 | if (defined($setup0->{Header}->{Options}->{shPassword})) { 39 | if ($setup0->{Header}->{Options}->{shPassword}) { 40 | if (!defined($password)) { 41 | return 0 42 | } 43 | my $digest = Digest->new('MD5'); 44 | my $hash; 45 | if (defined($setup0->{Header}->{PasswordHash})) { 46 | $hash = $setup0->{Header}->{PasswordHash}; 47 | $digest->add('PasswordCheckHash'); 48 | $digest->add(join('', map(chr, @{$setup0->{Header}->{PasswordSalt}}))); 49 | } else { 50 | $hash = $setup0->{Header}->{Password}; 51 | } 52 | if ($self->{IsUnicode}) { 53 | $digest->add(encode('UTF-16LE', $password)); 54 | } else { 55 | $digest->add(encode('cp1252', $password)); 56 | } 57 | return $digest->digest() eq $hash; 58 | } else { 59 | return !defined($password); 60 | } 61 | } else { 62 | return 1; 63 | } 64 | } 65 | 66 | 1; 67 | 68 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret5105.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret5105; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret4106); 7 | use Digest; 8 | use Win::Exe::Util; 9 | 10 | sub CheckFile { 11 | my ($self, $data, $location) = @_; 12 | # TODO Some versions might carry a SHA1 hash in Checksum 13 | if (defined($location->{Checksum})) { 14 | my $digest = Digest->new('CRC-32'); 15 | $digest->add($data); 16 | # CRC-32 produces a numeric result 17 | return $digest->digest() == $location->{Checksum}; 18 | } 19 | if (defined($location->{SHA1Sum})) { 20 | my $digest = Digest->new('SHA-1'); 21 | $digest->add($data); 22 | # SHA-1 produces a byte string result 23 | return $digest->digest() eq $location->{SHA1Sum}; 24 | } 25 | if (defined($location->{MD5Sum})) { 26 | my $digest = Digest->new('MD5'); 27 | $digest->add($data); 28 | # MD5 produces a byte string result 29 | return $digest->digest() eq $location->{MD5Sum}; 30 | } 31 | return 1; 32 | } 33 | 34 | sub OffsetTableSize { 35 | return 44; 36 | } 37 | 38 | sub ParseOffsetTable { 39 | my ($self, $data) = @_; 40 | (length($data) >= $self->OffsetTableSize()) || die("Invalid offset table size"); 41 | my $ofstable = unpackbinary($data, '(a12L8)<', 'ID', 'Version', 'TotalSize', 'OffsetEXE', 'UncompressedEXE', 'CRCEXE', 'Offset0', 'Offset1', 'TableCRC'); 42 | my $digest = Digest->new('CRC-32'); 43 | $digest->add(substr($data, 0, 40)); 44 | ($digest->digest() == $ofstable->{TableCRC}) || die("Checksum error"); 45 | return $ofstable; 46 | } 47 | 48 | sub TransformCallInstructions { 49 | my ($self, $data, $offset) = @_; 50 | if (length($data) < 5) { 51 | return $data; 52 | } 53 | if (!defined($offset)) { 54 | $offset = 0; 55 | } 56 | my $size = length($data) - 4; 57 | my $i = 0; 58 | while ($i < $size) { 59 | # Does it appear to be a CALL or JMP instruction with a relative 32-bit address? 60 | my $instr = ord(substr($data, $i, 1)); 61 | if ($instr == 0xe8 || $instr == 0xe9) { 62 | $i++; 63 | # Verify that the high byte of the address is $00 or $FF. If it isn't, then what we've encountered most likely isn't a CALL or JMP. 64 | my $arg = ord(substr($data, $i + 3, 1)); 65 | if ($arg == 0x00 || $arg == 0xff) { 66 | # Change the lower 3 bytes of the address to be relative to the beginning of the buffer, instead of to the next instruction. If decoding, do the opposite. 67 | my $addr = $offset + $i + 4; 68 | if ($i == 0x90000) { 69 | my $old = unpack('L', substr($data, $i, 4)); 70 | printf("instr:0x%02x addr:0x%08x old:0x%08x ", $instr, $addr, $old); 71 | } 72 | # if (!Encode) { 73 | $addr = -$addr; 74 | # } 75 | # Replace address 76 | for (my $x = 0; $x <= 2; $x++) { 77 | $addr += ord(substr($data, $i + $x, 1)); 78 | # Mask out the LSB or we might get a Unicode character... 79 | substr($data, $i + $x, 1) = chr($addr & 0xff); 80 | $addr >>= 8; 81 | } 82 | if ($i == 0x90000) { 83 | my $new = unpack('L', substr($data, $i, 4)); 84 | printf("new:0x%08x\n", $new); 85 | } 86 | } 87 | $i += 4; 88 | } else { 89 | $i++; 90 | } 91 | } 92 | return $data; 93 | } 94 | 95 | 1; 96 | 97 | -------------------------------------------------------------------------------- /Setup/Inno/Interpret5309.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Interpret5309; 4 | 5 | use strict; 6 | use base qw(Setup::Inno::Interpret5105); 7 | use Encode; 8 | 9 | sub TransformCallInstructions { 10 | my ($self, $data, $offset) = @_; 11 | if (length($data) < 5) { 12 | return $data; 13 | } 14 | if (!defined($offset)) { 15 | $offset = 0; 16 | } 17 | my $size = length($data) - 4; 18 | my $i = 0; 19 | #printf("Transforming %u bytes of binary data from offset 0x%08x\n", length($data) - 4, $offset); 20 | while ($i < $size) { 21 | #print("Remaining data: " . ($size - $i) . "\n"); 22 | # Does it appear to be a CALL or JMP instruction with a relative 32-bit address? 23 | my $instr = ord(substr($data, $i, 1)); 24 | if ($instr == 0xe8 || $instr == 0xe9) { 25 | $i++; 26 | # Verify that the high byte of the address is $00 or $FF. If it isn't, then what we've encountered most likely isn't a CALL or JMP. 27 | my $arg = ord(substr($data, $i + 3, 1)); 28 | if ($arg == 0x00 || $arg == 0xff) { 29 | # Change the lower 3 bytes of the address to be relative to the beginning of the buffer, instead of to the next instruction. If decoding, do the opposite. 30 | my $addr = ($offset + $i + 4) & 0xffffff; 31 | my $rel = ord(substr($data, $i, 1)) | (ord(substr($data, $i + 1, 1)) << 8) | (ord(substr($data, $i + 2, 1)) << 16); 32 | $rel -= $addr; 33 | # Debug 34 | my $old = unpack('L', substr($data, $i, 4)); 35 | #printf("instr:0x%02x addr:0x%08x rel:0x%08x old:0x%08x ", $instr, $addr, $rel, $old); 36 | # For a slightly higher compression ratio, we want the resulting high byte to be $00 for both forward and backward jumps. The high byte of the original relative address is likely to be the sign extension of bit 23, so if bit 23 is set, toggle all bits in the high byte. 37 | if ($rel & 0x800000) { 38 | substr($data, $i + 3, 1) = chr(~ord(substr($data, $i + 3, 1)) & 0xff); 39 | } 40 | substr($data, $i, 1) = chr($rel & 0xff); 41 | substr($data, $i + 1, 1) = chr(($rel >> 8) & 0xff); 42 | substr($data, $i + 2, 1) = chr(($rel >> 16) & 0xff); 43 | # Debug 44 | my $new = unpack('L', substr($data, $i, 4)); 45 | #printf("new:0x%08x\n", $new); 46 | } 47 | $i += 4; 48 | } else { 49 | $i++; 50 | } 51 | } 52 | return $data; 53 | } 54 | 55 | sub VerifyPassword { 56 | my ($self, $setup0, $password) = @_; 57 | if ($setup0->{Header}->{Options}->{shPassword}) { 58 | if (!defined($password)) { 59 | return 0 60 | } 61 | my $digest = Digest->new('SHA-1'); 62 | $digest->add('PasswordCheckHash'); 63 | $digest->add(join('', map(chr, @{$setup0->{Header}->{PasswordSalt}}))); 64 | if ($self->{IsUnicode}) { 65 | $digest->add(encode('UTF-16LE', $password)); 66 | } else { 67 | $digest->add(encode('cp1252', $password)); 68 | } 69 | return $digest->digest() eq $setup0->{Header}->{PasswordHash}; 70 | } else { 71 | return !defined($password); 72 | } 73 | } 74 | 75 | 1; 76 | 77 | -------------------------------------------------------------------------------- /Setup/Inno/LzmaReader.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::LzmaReader; 4 | 5 | use strict; 6 | require 5.006_000; 7 | use Carp; 8 | use Compress::Raw::Lzma; 9 | 10 | our $CHUNK_SIZE = 4096; 11 | 12 | sub new { 13 | my ($class, $reader, $size, $arch) = @_; 14 | 15 | # Read and dissect header 16 | $reader->read(my $header, 5) || croak("Can't read LZMA header"); 17 | my ($lclppb, $dictsize) = unpack('(CL)<', $header); 18 | my $pb = int($lclppb / 45); 19 | my $lclp = $lclppb - $pb * 45; 20 | my $lp = int($lclp / 9); 21 | my $lc = $lclp - $lp * 9; 22 | if (defined($arch)) { 23 | carp("Jump transform not supported"); 24 | } 25 | 26 | # Read all data into memory 27 | my $data; 28 | if (defined($size)) { 29 | # Subtract header first 30 | $size -= 5; 31 | # Consume as much as we're allowed to 32 | my $rdbytes = $reader->read($data, $size); 33 | ($rdbytes == $size) || croak("Didn't get all data from stream (expected $size, got $rdbytes)"); 34 | } else { 35 | # Consume everything the input gives us 36 | while (!$reader->eof()) { 37 | $reader->read(my $buffer, $CHUNK_SIZE) || croak("Can't read from stream"); 38 | $data .= $buffer; 39 | } 40 | } 41 | 42 | # Create a buffer decoder 43 | my ($decoder, $status) = Compress::Raw::Lzma::RawDecoder->new(Filter => [Lzma::Filter::Lzma1(DictSize => $dictsize, Lc => $lc, Lp => $lp, Pb => $pb)]); 44 | if (!defined($decoder)) { 45 | croak("Can't create LZMA decoder: " . $status); 46 | } 47 | 48 | # Uncompress data into memory 49 | $status = $decoder->code($data, my $uncompressed); 50 | if ($status != LZMA_OK && $status != LZMA_STREAM_END) { 51 | croak("Error uncompressing data: " . $status); 52 | } 53 | 54 | return IO::File->new(\$uncompressed, 'r') || croak("Can't create file handle for uncompressed data: $!"); 55 | } 56 | 57 | 58 | package Setup::Inno::Lzma2Reader; 59 | 60 | use strict; 61 | require 5.006_000; 62 | use Carp; 63 | use Compress::Raw::Lzma; 64 | use IO::Scalar; 65 | 66 | our $CHUNK_SIZE = 4096; 67 | 68 | sub new { 69 | my ($class, $reader, $size, $arch) = @_; 70 | 71 | # Read and dissect header 72 | $reader->read(my $header, 1) || croak("Can't read LZMA2 header"); 73 | my ($dictsizeflag) = unpack('C', $header); 74 | my $dictsize; 75 | if ($dictsizeflag > 40) { 76 | croak("Invalid dictionary size: $dictsizeflag"); 77 | } elsif ($dictsizeflag == 40) { 78 | $dictsize = 0xffffffff; 79 | } elsif ($dictsizeflag & 1) { 80 | $dictsize = 3 * (1 << (($dictsizeflag - 1) / 2 + 11)); 81 | } else { 82 | $dictsize = 1 << ($dictsizeflag / 2 + 12); 83 | } 84 | if (defined($arch)) { 85 | carp("Jump transform not supported"); 86 | } 87 | 88 | # Read all data into memory 89 | my $data; 90 | if (defined($size)) { 91 | # Subtract header first 92 | $size -= 1; 93 | # Consume as much as we're allowed to 94 | my $rdbytes = $reader->read($data, $size); 95 | ($rdbytes == $size) || croak("Didn't get all data from stream (expected $size, got $rdbytes)"); 96 | } else { 97 | # Consume everything the input gives us 98 | while (!$reader->eof()) { 99 | $reader->read(my $buffer, $CHUNK_SIZE) || croak("Can't read from stream"); 100 | $data .= $buffer; 101 | } 102 | } 103 | 104 | # Create a buffer decoder 105 | my ($decoder, $status) = Compress::Raw::Lzma::RawDecoder->new(Filter => [Lzma::Filter::Lzma2(DictSize => $dictsize)]); 106 | if (!defined($decoder)) { 107 | croak("Can't create LZMA2 decoder: " . $status); 108 | } 109 | 110 | # Uncompress data into memory 111 | $status = $decoder->code($data, my $uncompressed); 112 | if ($status != LZMA_OK && $status != LZMA_STREAM_END) { 113 | croak("Error uncompressing data: " . $status); 114 | } 115 | 116 | return IO::File->new(\$uncompressed, 'r') || croak("Can't create file handle for uncompressed data: $!"); 117 | } 118 | 119 | 120 | 1; 121 | -------------------------------------------------------------------------------- /Setup/Inno/Struct.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Setup::Inno::Struct; 4 | 5 | use strict; 6 | use base 'Setup::Inno::FieldReader'; 7 | 8 | # Override this method if your class name does not follow the usual scheme 9 | sub Version { 10 | my ($self) = @_; 11 | if (ref($self) =~ /^Setup::Inno::Struct([0-9]{4}u?)$/) { 12 | return $1; 13 | } 14 | return '0000'; 15 | } 16 | 17 | # Readers for various builtin types 18 | sub TFileTime { 19 | my ($self) = @_; 20 | my $tlow = $self->ReadLongWord(); 21 | my $thigh = $self->ReadLongWord(); 22 | my $hnsecs = $tlow | ($thigh << 32); 23 | my $secs = int($hnsecs / 10000000); 24 | my $nsecs = ($hnsecs - $secs * 10000000) * 100; 25 | return DateTime->new(year => 1601, month => 1, day => 1, hour => 0, minute => 0, second => 0, nanosecond => 0)->add(seconds => $secs, nanoseconds => $nsecs); 26 | } 27 | 28 | sub HKEY { 29 | return shift->ReadLongWord(); 30 | } 31 | 32 | sub DWORD { 33 | return shift->ReadLongWord(); 34 | } 35 | 36 | sub TSHA1Digest { 37 | return shift->ReadByteArray(20); 38 | } 39 | 40 | sub TMD5Digest { 41 | return shift->ReadByteArray(16); 42 | } 43 | 44 | 1; 45 | 46 | -------------------------------------------------------------------------------- /Setup/Inno/Struct1326.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct1326; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | UninstallIconName => $self->ReadString(1), 20 | BaseFilename => $self->ReadString(1), 21 | LicenseText => $self->ReadString(1), 22 | InfoBeforeText => $self->ReadString(1), 23 | InfoAfterText => $self->ReadString(1), 24 | UninstallFilesDir => $self->ReadString(1), 25 | UninstallDisplayName => $self->ReadString(1), 26 | UninstallDisplayIcon => $self->ReadString(1), 27 | AppMutex => $self->ReadString(1), 28 | NumDirEntries => $self->ReadInteger(), 29 | NumFileEntries => $self->ReadInteger(), 30 | NumFileLocationEntries => $self->ReadInteger(), 31 | NumIconEntries => $self->ReadInteger(), 32 | NumIniEntries => $self->ReadInteger(), 33 | NumRegistryEntries => $self->ReadInteger(), 34 | NumInstallDeleteEntries => $self->ReadInteger(), 35 | NumUninstallDeleteEntries => $self->ReadInteger(), 36 | NumRunEntries => $self->ReadInteger(), 37 | NumUninstallRunEntries => $self->ReadInteger(), 38 | MinVersion => { 39 | WinVersion => $self->ReadCardinal(), 40 | NTVersion => $self->ReadCardinal(), 41 | NTServicePack => $self->ReadWord(), 42 | }, 43 | OnlyBelowVersion => { 44 | WinVersion => $self->ReadCardinal(), 45 | NTVersion => $self->ReadCardinal(), 46 | NTServicePack => $self->ReadWord(), 47 | }, 48 | BackColor => $self->ReadLongInt(), 49 | BackColor2 => $self->ReadLongInt(), 50 | WizardImageBackColor => $self->ReadLongInt(), 51 | Password => $self->ReadLongInt(), 52 | ExtraDiskSpaceRequired => $self->ReadLongInt(), 53 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 54 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 55 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shAdminPrivilegesRequired', 'shAlwaysCreateUninstallIcon', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName' ]), 56 | }; 57 | return $ret; 58 | } 59 | sub TSetupDirEntry { 60 | my ($self) = @_; 61 | my $ret; 62 | $ret = { 63 | DirName => $self->ReadString(1), 64 | MinVersion => { 65 | WinVersion => $self->ReadCardinal(), 66 | NTVersion => $self->ReadCardinal(), 67 | NTServicePack => $self->ReadWord(), 68 | }, 69 | OnlyBelowVersion => { 70 | WinVersion => $self->ReadCardinal(), 71 | NTVersion => $self->ReadCardinal(), 72 | NTServicePack => $self->ReadWord(), 73 | }, 74 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 75 | }; 76 | return $ret; 77 | } 78 | sub TSetupFileEntry { 79 | my ($self) = @_; 80 | my $ret; 81 | $ret = { 82 | SourceFilename => $self->ReadString(1), 83 | DestName => $self->ReadString(1), 84 | InstallFontName => $self->ReadString(1), 85 | MinVersion => { 86 | WinVersion => $self->ReadCardinal(), 87 | NTVersion => $self->ReadCardinal(), 88 | NTServicePack => $self->ReadWord(), 89 | }, 90 | OnlyBelowVersion => { 91 | WinVersion => $self->ReadCardinal(), 92 | NTVersion => $self->ReadCardinal(), 93 | NTServicePack => $self->ReadWord(), 94 | }, 95 | LocationEntry => $self->ReadInteger(), 96 | Attribs => $self->ReadInteger(), 97 | ExternalSize => $self->ReadLongInt(), 98 | CopyMode => $self->ReadEnum([ 'cmNormal', 'cmIfDoesntExist', 'cmAlwaysOverwrite', 'cmAlwaysSkipIfSameOrOlder' ]), 99 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foIsReadmeFile', 'foCompareTimeStampAlso', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists' ]), 100 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 101 | }; 102 | return $ret; 103 | } 104 | sub TSetupIconEntry { 105 | my ($self) = @_; 106 | my $ret; 107 | $ret = { 108 | IconName => $self->ReadString(1), 109 | Filename => $self->ReadString(1), 110 | Parameters => $self->ReadString(1), 111 | WorkingDir => $self->ReadString(1), 112 | IconFilename => $self->ReadString(1), 113 | Comment => $self->ReadString(1), 114 | MinVersion => { 115 | WinVersion => $self->ReadCardinal(), 116 | NTVersion => $self->ReadCardinal(), 117 | NTServicePack => $self->ReadWord(), 118 | }, 119 | OnlyBelowVersion => { 120 | WinVersion => $self->ReadCardinal(), 121 | NTVersion => $self->ReadCardinal(), 122 | NTServicePack => $self->ReadWord(), 123 | }, 124 | IconIndex => $self->ReadInteger(), 125 | ShowCmd => $self->ReadInteger(), 126 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 127 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 128 | }; 129 | return $ret; 130 | } 131 | sub TSetupIniEntry { 132 | my ($self) = @_; 133 | my $ret; 134 | $ret = { 135 | Filename => $self->ReadString(1), 136 | Section => $self->ReadString(1), 137 | Entry => $self->ReadString(1), 138 | Value => $self->ReadString(1), 139 | MinVersion => { 140 | WinVersion => $self->ReadCardinal(), 141 | NTVersion => $self->ReadCardinal(), 142 | NTServicePack => $self->ReadWord(), 143 | }, 144 | OnlyBelowVersion => { 145 | WinVersion => $self->ReadCardinal(), 146 | NTVersion => $self->ReadCardinal(), 147 | NTServicePack => $self->ReadWord(), 148 | }, 149 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 150 | }; 151 | return $ret; 152 | } 153 | sub TSetupRegistryEntry { 154 | my ($self) = @_; 155 | my $ret; 156 | $ret = { 157 | Subkey => $self->ReadString(1), 158 | ValueName => $self->ReadString(1), 159 | ValueData => $self->ReadString(1), 160 | MinVersion => { 161 | WinVersion => $self->ReadCardinal(), 162 | NTVersion => $self->ReadCardinal(), 163 | NTServicePack => $self->ReadWord(), 164 | }, 165 | OnlyBelowVersion => { 166 | WinVersion => $self->ReadCardinal(), 167 | NTVersion => $self->ReadCardinal(), 168 | NTServicePack => $self->ReadWord(), 169 | }, 170 | RootKey => $self->HKEY(), 171 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 172 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 173 | }; 174 | return $ret; 175 | } 176 | sub TSetupDeleteEntry { 177 | my ($self) = @_; 178 | my $ret; 179 | $ret = { 180 | Name => $self->ReadString(1), 181 | MinVersion => { 182 | WinVersion => $self->ReadCardinal(), 183 | NTVersion => $self->ReadCardinal(), 184 | NTServicePack => $self->ReadWord(), 185 | }, 186 | OnlyBelowVersion => { 187 | WinVersion => $self->ReadCardinal(), 188 | NTVersion => $self->ReadCardinal(), 189 | NTServicePack => $self->ReadWord(), 190 | }, 191 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 192 | }; 193 | return $ret; 194 | } 195 | sub TSetupRunEntry { 196 | my ($self) = @_; 197 | my $ret; 198 | $ret = { 199 | Name => $self->ReadString(1), 200 | Parameters => $self->ReadString(1), 201 | WorkingDir => $self->ReadString(1), 202 | RunOnceId => $self->ReadString(1), 203 | MinVersion => { 204 | WinVersion => $self->ReadCardinal(), 205 | NTVersion => $self->ReadCardinal(), 206 | NTServicePack => $self->ReadWord(), 207 | }, 208 | OnlyBelowVersion => { 209 | WinVersion => $self->ReadCardinal(), 210 | NTVersion => $self->ReadCardinal(), 211 | NTServicePack => $self->ReadWord(), 212 | }, 213 | ShowCmd => $self->ReadInteger(), 214 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 215 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist' ]), 216 | }; 217 | return $ret; 218 | } 219 | sub TSetupFileLocationEntry { 220 | my ($self) = @_; 221 | my $ret; 222 | $ret = { 223 | FirstDisk => $self->ReadInteger(), 224 | LastDisk => $self->ReadInteger(), 225 | StartOffset => $self->ReadLongInt(), 226 | OriginalSize => $self->ReadLongInt(), 227 | CompressedSize => $self->ReadLongInt(), 228 | Adler => $self->ReadLongInt(), 229 | Date => $self->TFileTime(), 230 | FileVersionMS => $self->DWORD(), 231 | FileVersionLS => $self->DWORD(), 232 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid' ]), 233 | }; 234 | return $ret; 235 | } 236 | 1; 237 | -------------------------------------------------------------------------------- /Setup/Inno/Struct2019.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct2019; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | UninstallIconName => $self->ReadString(1), 20 | BaseFilename => $self->ReadString(1), 21 | LicenseText => $self->ReadString(1), 22 | InfoBeforeText => $self->ReadString(1), 23 | InfoAfterText => $self->ReadString(1), 24 | UninstallFilesDir => $self->ReadString(1), 25 | UninstallDisplayName => $self->ReadString(1), 26 | UninstallDisplayIcon => $self->ReadString(1), 27 | AppMutex => $self->ReadString(1), 28 | LeadBytes => $self->ReadSet(256), 29 | NumTypeEntries => $self->ReadInteger(), 30 | NumComponentEntries => $self->ReadInteger(), 31 | NumTaskEntries => $self->ReadInteger(), 32 | NumDirEntries => $self->ReadInteger(), 33 | NumFileEntries => $self->ReadInteger(), 34 | NumFileLocationEntries => $self->ReadInteger(), 35 | NumIconEntries => $self->ReadInteger(), 36 | NumIniEntries => $self->ReadInteger(), 37 | NumRegistryEntries => $self->ReadInteger(), 38 | NumInstallDeleteEntries => $self->ReadInteger(), 39 | NumUninstallDeleteEntries => $self->ReadInteger(), 40 | NumRunEntries => $self->ReadInteger(), 41 | NumUninstallRunEntries => $self->ReadInteger(), 42 | MinVersion => { 43 | WinVersion => $self->ReadCardinal(), 44 | NTVersion => $self->ReadCardinal(), 45 | NTServicePack => $self->ReadWord(), 46 | }, 47 | OnlyBelowVersion => { 48 | WinVersion => $self->ReadCardinal(), 49 | NTVersion => $self->ReadCardinal(), 50 | NTServicePack => $self->ReadWord(), 51 | }, 52 | BackColor => $self->ReadLongInt(), 53 | BackColor2 => $self->ReadLongInt(), 54 | WizardImageBackColor => $self->ReadLongInt(), 55 | WizardSmallImageBackColor => $self->ReadLongInt(), 56 | Password => $self->ReadLongInt(), 57 | ExtraDiskSpaceRequired => $self->ReadLongInt(), 58 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 59 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 60 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 61 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 62 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shAdminPrivilegesRequired', 'shAlwaysCreateUninstallIcon', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath' ]), 63 | }; 64 | return $ret; 65 | } 66 | sub TSetupTypeEntry { 67 | my ($self) = @_; 68 | my $ret; 69 | $ret = { 70 | Name => $self->ReadString(1), 71 | Description => $self->ReadString(1), 72 | MinVersion => { 73 | WinVersion => $self->ReadCardinal(), 74 | NTVersion => $self->ReadCardinal(), 75 | NTServicePack => $self->ReadWord(), 76 | }, 77 | OnlyBelowVersion => { 78 | WinVersion => $self->ReadCardinal(), 79 | NTVersion => $self->ReadCardinal(), 80 | NTServicePack => $self->ReadWord(), 81 | }, 82 | Options => $self->ReadSet([ 'toIsCustom' ]), 83 | Size => $self->ReadLongInt(), 84 | }; 85 | return $ret; 86 | } 87 | sub TSetupComponentEntry { 88 | my ($self) = @_; 89 | my $ret; 90 | $ret = { 91 | Name => $self->ReadString(1), 92 | Description => $self->ReadString(1), 93 | Types => $self->ReadString(1), 94 | ExtraDiskSpaceRequired => $self->ReadLongInt(), 95 | MinVersion => { 96 | WinVersion => $self->ReadCardinal(), 97 | NTVersion => $self->ReadCardinal(), 98 | NTServicePack => $self->ReadWord(), 99 | }, 100 | OnlyBelowVersion => { 101 | WinVersion => $self->ReadCardinal(), 102 | NTVersion => $self->ReadCardinal(), 103 | NTServicePack => $self->ReadWord(), 104 | }, 105 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning' ]), 106 | Size => $self->ReadLongInt(), 107 | }; 108 | return $ret; 109 | } 110 | sub TSetupTaskEntry { 111 | my ($self) = @_; 112 | my $ret; 113 | $ret = { 114 | Name => $self->ReadString(1), 115 | Description => $self->ReadString(1), 116 | GroupDescription => $self->ReadString(1), 117 | Components => $self->ReadString(1), 118 | MinVersion => { 119 | WinVersion => $self->ReadCardinal(), 120 | NTVersion => $self->ReadCardinal(), 121 | NTServicePack => $self->ReadWord(), 122 | }, 123 | OnlyBelowVersion => { 124 | WinVersion => $self->ReadCardinal(), 125 | NTVersion => $self->ReadCardinal(), 126 | NTServicePack => $self->ReadWord(), 127 | }, 128 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 129 | }; 130 | return $ret; 131 | } 132 | sub TSetupDirEntry { 133 | my ($self) = @_; 134 | my $ret; 135 | $ret = { 136 | DirName => $self->ReadString(1), 137 | Components => $self->ReadString(1), 138 | Tasks => $self->ReadString(1), 139 | Attribs => $self->ReadInteger(), 140 | MinVersion => { 141 | WinVersion => $self->ReadCardinal(), 142 | NTVersion => $self->ReadCardinal(), 143 | NTServicePack => $self->ReadWord(), 144 | }, 145 | OnlyBelowVersion => { 146 | WinVersion => $self->ReadCardinal(), 147 | NTVersion => $self->ReadCardinal(), 148 | NTServicePack => $self->ReadWord(), 149 | }, 150 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 151 | }; 152 | return $ret; 153 | } 154 | sub TSetupFileEntry { 155 | my ($self) = @_; 156 | my $ret; 157 | $ret = { 158 | SourceFilename => $self->ReadString(1), 159 | DestName => $self->ReadString(1), 160 | InstallFontName => $self->ReadString(1), 161 | Components => $self->ReadString(1), 162 | Tasks => $self->ReadString(1), 163 | MinVersion => { 164 | WinVersion => $self->ReadCardinal(), 165 | NTVersion => $self->ReadCardinal(), 166 | NTServicePack => $self->ReadWord(), 167 | }, 168 | OnlyBelowVersion => { 169 | WinVersion => $self->ReadCardinal(), 170 | NTVersion => $self->ReadCardinal(), 171 | NTServicePack => $self->ReadWord(), 172 | }, 173 | LocationEntry => $self->ReadInteger(), 174 | Attribs => $self->ReadInteger(), 175 | ExternalSize => $self->ReadLongInt(), 176 | CopyMode => $self->ReadEnum([ 'cmNormal', 'cmIfDoesntExist', 'cmAlwaysOverwrite', 'cmAlwaysSkipIfSameOrOlder' ]), 177 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStampAlso', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError' ]), 178 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 179 | }; 180 | return $ret; 181 | } 182 | sub TSetupIconEntry { 183 | my ($self) = @_; 184 | my $ret; 185 | $ret = { 186 | IconName => $self->ReadString(1), 187 | Filename => $self->ReadString(1), 188 | Parameters => $self->ReadString(1), 189 | WorkingDir => $self->ReadString(1), 190 | IconFilename => $self->ReadString(1), 191 | Comment => $self->ReadString(1), 192 | Components => $self->ReadString(1), 193 | Tasks => $self->ReadString(1), 194 | MinVersion => { 195 | WinVersion => $self->ReadCardinal(), 196 | NTVersion => $self->ReadCardinal(), 197 | NTServicePack => $self->ReadWord(), 198 | }, 199 | OnlyBelowVersion => { 200 | WinVersion => $self->ReadCardinal(), 201 | NTVersion => $self->ReadCardinal(), 202 | NTServicePack => $self->ReadWord(), 203 | }, 204 | IconIndex => $self->ReadInteger(), 205 | ShowCmd => $self->ReadInteger(), 206 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 207 | HotKey => $self->ReadWord(), 208 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 209 | }; 210 | return $ret; 211 | } 212 | sub TSetupIniEntry { 213 | my ($self) = @_; 214 | my $ret; 215 | $ret = { 216 | Filename => $self->ReadString(1), 217 | Section => $self->ReadString(1), 218 | Entry => $self->ReadString(1), 219 | Value => $self->ReadString(1), 220 | Components => $self->ReadString(1), 221 | Tasks => $self->ReadString(1), 222 | MinVersion => { 223 | WinVersion => $self->ReadCardinal(), 224 | NTVersion => $self->ReadCardinal(), 225 | NTServicePack => $self->ReadWord(), 226 | }, 227 | OnlyBelowVersion => { 228 | WinVersion => $self->ReadCardinal(), 229 | NTVersion => $self->ReadCardinal(), 230 | NTServicePack => $self->ReadWord(), 231 | }, 232 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 233 | }; 234 | return $ret; 235 | } 236 | sub TSetupRegistryEntry { 237 | my ($self) = @_; 238 | my $ret; 239 | $ret = { 240 | Subkey => $self->ReadString(1), 241 | ValueName => $self->ReadString(1), 242 | ValueData => $self->ReadString(1), 243 | Components => $self->ReadString(1), 244 | Tasks => $self->ReadString(1), 245 | MinVersion => { 246 | WinVersion => $self->ReadCardinal(), 247 | NTVersion => $self->ReadCardinal(), 248 | NTServicePack => $self->ReadWord(), 249 | }, 250 | OnlyBelowVersion => { 251 | WinVersion => $self->ReadCardinal(), 252 | NTVersion => $self->ReadCardinal(), 253 | NTServicePack => $self->ReadWord(), 254 | }, 255 | RootKey => $self->HKEY(), 256 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 257 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 258 | }; 259 | return $ret; 260 | } 261 | sub TSetupDeleteEntry { 262 | my ($self) = @_; 263 | my $ret; 264 | $ret = { 265 | Name => $self->ReadString(1), 266 | Components => $self->ReadString(1), 267 | Tasks => $self->ReadString(1), 268 | MinVersion => { 269 | WinVersion => $self->ReadCardinal(), 270 | NTVersion => $self->ReadCardinal(), 271 | NTServicePack => $self->ReadWord(), 272 | }, 273 | OnlyBelowVersion => { 274 | WinVersion => $self->ReadCardinal(), 275 | NTVersion => $self->ReadCardinal(), 276 | NTServicePack => $self->ReadWord(), 277 | }, 278 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 279 | }; 280 | return $ret; 281 | } 282 | sub TSetupRunEntry { 283 | my ($self) = @_; 284 | my $ret; 285 | $ret = { 286 | Name => $self->ReadString(1), 287 | Parameters => $self->ReadString(1), 288 | WorkingDir => $self->ReadString(1), 289 | RunOnceId => $self->ReadString(1), 290 | StatusMsg => $self->ReadString(1), 291 | Description => $self->ReadString(1), 292 | Components => $self->ReadString(1), 293 | Tasks => $self->ReadString(1), 294 | MinVersion => { 295 | WinVersion => $self->ReadCardinal(), 296 | NTVersion => $self->ReadCardinal(), 297 | NTServicePack => $self->ReadWord(), 298 | }, 299 | OnlyBelowVersion => { 300 | WinVersion => $self->ReadCardinal(), 301 | NTVersion => $self->ReadCardinal(), 302 | NTServicePack => $self->ReadWord(), 303 | }, 304 | ShowCmd => $self->ReadInteger(), 305 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 306 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 307 | }; 308 | return $ret; 309 | } 310 | sub TSetupFileLocationEntry { 311 | my ($self) = @_; 312 | my $ret; 313 | $ret = { 314 | FirstDisk => $self->ReadInteger(), 315 | LastDisk => $self->ReadInteger(), 316 | StartOffset => $self->ReadLongInt(), 317 | OriginalSize => $self->ReadLongInt(), 318 | CompressedSize => $self->ReadLongInt(), 319 | Adler => $self->ReadLongInt(), 320 | Date => $self->TFileTime(), 321 | FileVersionMS => $self->DWORD(), 322 | FileVersionLS => $self->DWORD(), 323 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid', 'foBzipped' ]), 324 | }; 325 | return $ret; 326 | } 327 | 1; 328 | -------------------------------------------------------------------------------- /Setup/Inno/Struct3007.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct3007; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | BaseFilename => $self->ReadString(1), 20 | LicenseText => $self->ReadString(1), 21 | InfoBeforeText => $self->ReadString(1), 22 | InfoAfterText => $self->ReadString(1), 23 | UninstallFilesDir => $self->ReadString(1), 24 | UninstallDisplayName => $self->ReadString(1), 25 | UninstallDisplayIcon => $self->ReadString(1), 26 | AppMutex => $self->ReadString(1), 27 | DefaultUserInfoName => $self->ReadString(1), 28 | DefaultUserInfoOrg => $self->ReadString(1), 29 | LeadBytes => $self->ReadSet(256), 30 | NumTypeEntries => $self->ReadInteger(), 31 | NumComponentEntries => $self->ReadInteger(), 32 | NumTaskEntries => $self->ReadInteger(), 33 | NumDirEntries => $self->ReadInteger(), 34 | NumFileEntries => $self->ReadInteger(), 35 | NumFileLocationEntries => $self->ReadInteger(), 36 | NumIconEntries => $self->ReadInteger(), 37 | NumIniEntries => $self->ReadInteger(), 38 | NumRegistryEntries => $self->ReadInteger(), 39 | NumInstallDeleteEntries => $self->ReadInteger(), 40 | NumUninstallDeleteEntries => $self->ReadInteger(), 41 | NumRunEntries => $self->ReadInteger(), 42 | NumUninstallRunEntries => $self->ReadInteger(), 43 | MinVersion => { 44 | WinVersion => $self->ReadCardinal(), 45 | NTVersion => $self->ReadCardinal(), 46 | NTServicePack => $self->ReadWord(), 47 | }, 48 | OnlyBelowVersion => { 49 | WinVersion => $self->ReadCardinal(), 50 | NTVersion => $self->ReadCardinal(), 51 | NTServicePack => $self->ReadWord(), 52 | }, 53 | BackColor => $self->ReadLongInt(), 54 | BackColor2 => $self->ReadLongInt(), 55 | WizardImageBackColor => $self->ReadLongInt(), 56 | WizardSmallImageBackColor => $self->ReadLongInt(), 57 | Password => $self->ReadLongInt(), 58 | ExtraDiskSpaceRequired => $self->ReadLongInt(), 59 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 60 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 61 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 62 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 63 | PrivilegesRequired => $self->ReadEnum([ 'prNone', 'prPowerUser', 'prAdmin' ]), 64 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath', 'shUserInfoPage', 'shUsePreviousUserInfo', 'shUninstallRestartComputer', 'shRestartIfNeededByRun' ]), 65 | }; 66 | return $ret; 67 | } 68 | sub TSetupTypeEntry { 69 | my ($self) = @_; 70 | my $ret; 71 | $ret = { 72 | Name => $self->ReadString(1), 73 | Description => $self->ReadString(1), 74 | MinVersion => { 75 | WinVersion => $self->ReadCardinal(), 76 | NTVersion => $self->ReadCardinal(), 77 | NTServicePack => $self->ReadWord(), 78 | }, 79 | OnlyBelowVersion => { 80 | WinVersion => $self->ReadCardinal(), 81 | NTVersion => $self->ReadCardinal(), 82 | NTServicePack => $self->ReadWord(), 83 | }, 84 | Options => $self->ReadSet([ 'toIsCustom' ]), 85 | Size => $self->ReadLongInt(), 86 | }; 87 | return $ret; 88 | } 89 | sub TSetupComponentEntry { 90 | my ($self) = @_; 91 | my $ret; 92 | $ret = { 93 | Name => $self->ReadString(1), 94 | Description => $self->ReadString(1), 95 | Types => $self->ReadString(1), 96 | ExtraDiskSpaceRequired => $self->ReadLongInt(), 97 | MinVersion => { 98 | WinVersion => $self->ReadCardinal(), 99 | NTVersion => $self->ReadCardinal(), 100 | NTServicePack => $self->ReadWord(), 101 | }, 102 | OnlyBelowVersion => { 103 | WinVersion => $self->ReadCardinal(), 104 | NTVersion => $self->ReadCardinal(), 105 | NTServicePack => $self->ReadWord(), 106 | }, 107 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning' ]), 108 | Size => $self->ReadLongInt(), 109 | }; 110 | return $ret; 111 | } 112 | sub TSetupTaskEntry { 113 | my ($self) = @_; 114 | my $ret; 115 | $ret = { 116 | Name => $self->ReadString(1), 117 | Description => $self->ReadString(1), 118 | GroupDescription => $self->ReadString(1), 119 | Components => $self->ReadString(1), 120 | MinVersion => { 121 | WinVersion => $self->ReadCardinal(), 122 | NTVersion => $self->ReadCardinal(), 123 | NTServicePack => $self->ReadWord(), 124 | }, 125 | OnlyBelowVersion => { 126 | WinVersion => $self->ReadCardinal(), 127 | NTVersion => $self->ReadCardinal(), 128 | NTServicePack => $self->ReadWord(), 129 | }, 130 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 131 | }; 132 | return $ret; 133 | } 134 | sub TSetupDirEntry { 135 | my ($self) = @_; 136 | my $ret; 137 | $ret = { 138 | DirName => $self->ReadString(1), 139 | Components => $self->ReadString(1), 140 | Tasks => $self->ReadString(1), 141 | Attribs => $self->ReadInteger(), 142 | MinVersion => { 143 | WinVersion => $self->ReadCardinal(), 144 | NTVersion => $self->ReadCardinal(), 145 | NTServicePack => $self->ReadWord(), 146 | }, 147 | OnlyBelowVersion => { 148 | WinVersion => $self->ReadCardinal(), 149 | NTVersion => $self->ReadCardinal(), 150 | NTServicePack => $self->ReadWord(), 151 | }, 152 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 153 | }; 154 | return $ret; 155 | } 156 | sub TSetupFileEntry { 157 | my ($self) = @_; 158 | my $ret; 159 | $ret = { 160 | SourceFilename => $self->ReadString(1), 161 | DestName => $self->ReadString(1), 162 | InstallFontName => $self->ReadString(1), 163 | Components => $self->ReadString(1), 164 | Tasks => $self->ReadString(1), 165 | MinVersion => { 166 | WinVersion => $self->ReadCardinal(), 167 | NTVersion => $self->ReadCardinal(), 168 | NTServicePack => $self->ReadWord(), 169 | }, 170 | OnlyBelowVersion => { 171 | WinVersion => $self->ReadCardinal(), 172 | NTVersion => $self->ReadCardinal(), 173 | NTServicePack => $self->ReadWord(), 174 | }, 175 | LocationEntry => $self->ReadInteger(), 176 | Attribs => $self->ReadInteger(), 177 | ExternalSize => $self->ReadLongInt(), 178 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStamp', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError', 'foUninsRestartDelete', 'foOnlyIfDoesntExist', 'foIgnoreVersion', 'foPromptIfOlder' ]), 179 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 180 | }; 181 | return $ret; 182 | } 183 | sub TSetupIconEntry { 184 | my ($self) = @_; 185 | my $ret; 186 | $ret = { 187 | IconName => $self->ReadString(1), 188 | Filename => $self->ReadString(1), 189 | Parameters => $self->ReadString(1), 190 | WorkingDir => $self->ReadString(1), 191 | IconFilename => $self->ReadString(1), 192 | Comment => $self->ReadString(1), 193 | Components => $self->ReadString(1), 194 | Tasks => $self->ReadString(1), 195 | MinVersion => { 196 | WinVersion => $self->ReadCardinal(), 197 | NTVersion => $self->ReadCardinal(), 198 | NTServicePack => $self->ReadWord(), 199 | }, 200 | OnlyBelowVersion => { 201 | WinVersion => $self->ReadCardinal(), 202 | NTVersion => $self->ReadCardinal(), 203 | NTServicePack => $self->ReadWord(), 204 | }, 205 | IconIndex => $self->ReadInteger(), 206 | ShowCmd => $self->ReadInteger(), 207 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 208 | HotKey => $self->ReadWord(), 209 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 210 | }; 211 | return $ret; 212 | } 213 | sub TSetupIniEntry { 214 | my ($self) = @_; 215 | my $ret; 216 | $ret = { 217 | Filename => $self->ReadString(1), 218 | Section => $self->ReadString(1), 219 | Entry => $self->ReadString(1), 220 | Value => $self->ReadString(1), 221 | Components => $self->ReadString(1), 222 | Tasks => $self->ReadString(1), 223 | MinVersion => { 224 | WinVersion => $self->ReadCardinal(), 225 | NTVersion => $self->ReadCardinal(), 226 | NTServicePack => $self->ReadWord(), 227 | }, 228 | OnlyBelowVersion => { 229 | WinVersion => $self->ReadCardinal(), 230 | NTVersion => $self->ReadCardinal(), 231 | NTServicePack => $self->ReadWord(), 232 | }, 233 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 234 | }; 235 | return $ret; 236 | } 237 | sub TSetupRegistryEntry { 238 | my ($self) = @_; 239 | my $ret; 240 | $ret = { 241 | Subkey => $self->ReadString(1), 242 | ValueName => $self->ReadString(1), 243 | ValueData => $self->ReadString(1), 244 | Components => $self->ReadString(1), 245 | Tasks => $self->ReadString(1), 246 | MinVersion => { 247 | WinVersion => $self->ReadCardinal(), 248 | NTVersion => $self->ReadCardinal(), 249 | NTServicePack => $self->ReadWord(), 250 | }, 251 | OnlyBelowVersion => { 252 | WinVersion => $self->ReadCardinal(), 253 | NTVersion => $self->ReadCardinal(), 254 | NTServicePack => $self->ReadWord(), 255 | }, 256 | RootKey => $self->HKEY(), 257 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 258 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 259 | }; 260 | return $ret; 261 | } 262 | sub TSetupDeleteEntry { 263 | my ($self) = @_; 264 | my $ret; 265 | $ret = { 266 | Name => $self->ReadString(1), 267 | Components => $self->ReadString(1), 268 | Tasks => $self->ReadString(1), 269 | MinVersion => { 270 | WinVersion => $self->ReadCardinal(), 271 | NTVersion => $self->ReadCardinal(), 272 | NTServicePack => $self->ReadWord(), 273 | }, 274 | OnlyBelowVersion => { 275 | WinVersion => $self->ReadCardinal(), 276 | NTVersion => $self->ReadCardinal(), 277 | NTServicePack => $self->ReadWord(), 278 | }, 279 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 280 | }; 281 | return $ret; 282 | } 283 | sub TSetupRunEntry { 284 | my ($self) = @_; 285 | my $ret; 286 | $ret = { 287 | Name => $self->ReadString(1), 288 | Parameters => $self->ReadString(1), 289 | WorkingDir => $self->ReadString(1), 290 | RunOnceId => $self->ReadString(1), 291 | StatusMsg => $self->ReadString(1), 292 | Description => $self->ReadString(1), 293 | Components => $self->ReadString(1), 294 | Tasks => $self->ReadString(1), 295 | MinVersion => { 296 | WinVersion => $self->ReadCardinal(), 297 | NTVersion => $self->ReadCardinal(), 298 | NTServicePack => $self->ReadWord(), 299 | }, 300 | OnlyBelowVersion => { 301 | WinVersion => $self->ReadCardinal(), 302 | NTVersion => $self->ReadCardinal(), 303 | NTServicePack => $self->ReadWord(), 304 | }, 305 | ShowCmd => $self->ReadInteger(), 306 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 307 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 308 | }; 309 | return $ret; 310 | } 311 | sub TSetupFileLocationEntry { 312 | my ($self) = @_; 313 | my $ret; 314 | $ret = { 315 | FirstDisk => $self->ReadInteger(), 316 | LastDisk => $self->ReadInteger(), 317 | StartOffset => $self->ReadLongInt(), 318 | OriginalSize => $self->ReadLongInt(), 319 | CompressedSize => $self->ReadLongInt(), 320 | Adler => $self->ReadLongInt(), 321 | Date => $self->TFileTime(), 322 | FileVersionMS => $self->DWORD(), 323 | FileVersionLS => $self->DWORD(), 324 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid', 'foBzipped' ]), 325 | }; 326 | return $ret; 327 | } 328 | 1; 329 | -------------------------------------------------------------------------------- /Setup/Inno/Struct4008.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct4008; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | BaseFilename => $self->ReadString(1), 20 | LicenseText => $self->ReadString(1), 21 | InfoBeforeText => $self->ReadString(1), 22 | InfoAfterText => $self->ReadString(1), 23 | UninstallFilesDir => $self->ReadString(1), 24 | UninstallDisplayName => $self->ReadString(1), 25 | UninstallDisplayIcon => $self->ReadString(1), 26 | AppMutex => $self->ReadString(1), 27 | DefaultUserInfoName => $self->ReadString(1), 28 | DefaultUserInfoOrg => $self->ReadString(1), 29 | DefaultUserInfoSerial => $self->ReadString(1), 30 | CompiledCodeText => $self->ReadString(1), 31 | LeadBytes => $self->ReadSet(256), 32 | NumLanguageEntries => $self->ReadInteger(), 33 | NumTypeEntries => $self->ReadInteger(), 34 | NumComponentEntries => $self->ReadInteger(), 35 | NumTaskEntries => $self->ReadInteger(), 36 | NumDirEntries => $self->ReadInteger(), 37 | NumFileEntries => $self->ReadInteger(), 38 | NumFileLocationEntries => $self->ReadInteger(), 39 | NumIconEntries => $self->ReadInteger(), 40 | NumIniEntries => $self->ReadInteger(), 41 | NumRegistryEntries => $self->ReadInteger(), 42 | NumInstallDeleteEntries => $self->ReadInteger(), 43 | NumUninstallDeleteEntries => $self->ReadInteger(), 44 | NumRunEntries => $self->ReadInteger(), 45 | NumUninstallRunEntries => $self->ReadInteger(), 46 | MinVersion => { 47 | WinVersion => $self->ReadCardinal(), 48 | NTVersion => $self->ReadCardinal(), 49 | NTServicePack => $self->ReadWord(), 50 | }, 51 | OnlyBelowVersion => { 52 | WinVersion => $self->ReadCardinal(), 53 | NTVersion => $self->ReadCardinal(), 54 | NTServicePack => $self->ReadWord(), 55 | }, 56 | BackColor => $self->ReadLongInt(), 57 | BackColor2 => $self->ReadLongInt(), 58 | WizardImageBackColor => $self->ReadLongInt(), 59 | WizardSmallImageBackColor => $self->ReadLongInt(), 60 | Password => $self->ReadLongInt(), 61 | ExtraDiskSpaceRequired => $self->ReadInt64(), 62 | SlicesPerDisk => $self->ReadInteger(), 63 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 64 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 65 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 66 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 67 | PrivilegesRequired => $self->ReadEnum([ 'prNone', 'prPowerUser', 'prAdmin' ]), 68 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath', 'shUserInfoPage', 'shUsePreviousUserInfo', 'shUninstallRestartComputer', 'shRestartIfNeededByRun', 'shShowTasksTreeLines', 'shShowLanguageDialog', 'shDetectLanguageUsingLocale' ]), 69 | }; 70 | return $ret; 71 | } 72 | sub TSetupLanguageEntry { 73 | my ($self) = @_; 74 | my $ret; 75 | $ret = { 76 | Name => $self->ReadString(1), 77 | LanguageName => $self->ReadString(1), 78 | DialogFontName => $self->ReadString(1), 79 | TitleFontName => $self->ReadString(1), 80 | WelcomeFontName => $self->ReadString(1), 81 | CopyrightFontName => $self->ReadString(1), 82 | Data => $self->ReadString(1), 83 | LicenseText => $self->ReadString(1), 84 | InfoBeforeText => $self->ReadString(1), 85 | InfoAfterText => $self->ReadString(1), 86 | LanguageID => $self->ReadCardinal(), 87 | DialogFontSize => $self->ReadInteger(), 88 | DialogFontStandardHeight => $self->ReadInteger(), 89 | TitleFontSize => $self->ReadInteger(), 90 | WelcomeFontSize => $self->ReadInteger(), 91 | CopyrightFontSize => $self->ReadInteger(), 92 | }; 93 | return $ret; 94 | } 95 | sub TSetupTypeEntry { 96 | my ($self) = @_; 97 | my $ret; 98 | $ret = { 99 | Name => $self->ReadString(1), 100 | Description => $self->ReadString(1), 101 | Languages => $self->ReadString(1), 102 | Check => $self->ReadString(1), 103 | MinVersion => { 104 | WinVersion => $self->ReadCardinal(), 105 | NTVersion => $self->ReadCardinal(), 106 | NTServicePack => $self->ReadWord(), 107 | }, 108 | OnlyBelowVersion => { 109 | WinVersion => $self->ReadCardinal(), 110 | NTVersion => $self->ReadCardinal(), 111 | NTServicePack => $self->ReadWord(), 112 | }, 113 | Options => $self->ReadSet([ 'toIsCustom' ]), 114 | Typ => $self->ReadEnum([ 'ttUser', 'ttDefaultFull', 'ttDefaultCompact', 'ttDefaultCustom' ]), 115 | Size => $self->ReadInt64(), 116 | }; 117 | return $ret; 118 | } 119 | sub TSetupComponentEntry { 120 | my ($self) = @_; 121 | my $ret; 122 | $ret = { 123 | Name => $self->ReadString(1), 124 | Description => $self->ReadString(1), 125 | Types => $self->ReadString(1), 126 | Languages => $self->ReadString(1), 127 | Check => $self->ReadString(1), 128 | ExtraDiskSpaceRequired => $self->ReadInt64(), 129 | Level => $self->ReadInteger(), 130 | Used => $self->ReadByte(), 131 | MinVersion => { 132 | WinVersion => $self->ReadCardinal(), 133 | NTVersion => $self->ReadCardinal(), 134 | NTServicePack => $self->ReadWord(), 135 | }, 136 | OnlyBelowVersion => { 137 | WinVersion => $self->ReadCardinal(), 138 | NTVersion => $self->ReadCardinal(), 139 | NTServicePack => $self->ReadWord(), 140 | }, 141 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning', 'coExclusive' ]), 142 | Size => $self->ReadInt64(), 143 | }; 144 | return $ret; 145 | } 146 | sub TSetupTaskEntry { 147 | my ($self) = @_; 148 | my $ret; 149 | $ret = { 150 | Name => $self->ReadString(1), 151 | Description => $self->ReadString(1), 152 | GroupDescription => $self->ReadString(1), 153 | Components => $self->ReadString(1), 154 | Languages => $self->ReadString(1), 155 | Check => $self->ReadString(1), 156 | Level => $self->ReadInteger(), 157 | Used => $self->ReadByte(), 158 | MinVersion => { 159 | WinVersion => $self->ReadCardinal(), 160 | NTVersion => $self->ReadCardinal(), 161 | NTServicePack => $self->ReadWord(), 162 | }, 163 | OnlyBelowVersion => { 164 | WinVersion => $self->ReadCardinal(), 165 | NTVersion => $self->ReadCardinal(), 166 | NTServicePack => $self->ReadWord(), 167 | }, 168 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 169 | }; 170 | return $ret; 171 | } 172 | sub TSetupDirEntry { 173 | my ($self) = @_; 174 | my $ret; 175 | $ret = { 176 | DirName => $self->ReadString(1), 177 | Components => $self->ReadString(1), 178 | Tasks => $self->ReadString(1), 179 | Languages => $self->ReadString(1), 180 | Check => $self->ReadString(1), 181 | Attribs => $self->ReadInteger(), 182 | MinVersion => { 183 | WinVersion => $self->ReadCardinal(), 184 | NTVersion => $self->ReadCardinal(), 185 | NTServicePack => $self->ReadWord(), 186 | }, 187 | OnlyBelowVersion => { 188 | WinVersion => $self->ReadCardinal(), 189 | NTVersion => $self->ReadCardinal(), 190 | NTServicePack => $self->ReadWord(), 191 | }, 192 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 193 | }; 194 | return $ret; 195 | } 196 | sub TSetupFileEntry { 197 | my ($self) = @_; 198 | my $ret; 199 | $ret = { 200 | SourceFilename => $self->ReadString(1), 201 | DestName => $self->ReadString(1), 202 | InstallFontName => $self->ReadString(1), 203 | Components => $self->ReadString(1), 204 | Tasks => $self->ReadString(1), 205 | Languages => $self->ReadString(1), 206 | Check => $self->ReadString(1), 207 | MinVersion => { 208 | WinVersion => $self->ReadCardinal(), 209 | NTVersion => $self->ReadCardinal(), 210 | NTServicePack => $self->ReadWord(), 211 | }, 212 | OnlyBelowVersion => { 213 | WinVersion => $self->ReadCardinal(), 214 | NTVersion => $self->ReadCardinal(), 215 | NTServicePack => $self->ReadWord(), 216 | }, 217 | LocationEntry => $self->ReadInteger(), 218 | Attribs => $self->ReadInteger(), 219 | ExternalSize => $self->ReadInt64(), 220 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStamp', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError', 'foUninsRestartDelete', 'foOnlyIfDoesntExist', 'foIgnoreVersion', 'foPromptIfOlder', 'foDontCopy', 'foUninsRemoveReadOnly' ]), 221 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 222 | }; 223 | return $ret; 224 | } 225 | sub TSetupIconEntry { 226 | my ($self) = @_; 227 | my $ret; 228 | $ret = { 229 | IconName => $self->ReadString(1), 230 | Filename => $self->ReadString(1), 231 | Parameters => $self->ReadString(1), 232 | WorkingDir => $self->ReadString(1), 233 | IconFilename => $self->ReadString(1), 234 | Comment => $self->ReadString(1), 235 | Components => $self->ReadString(1), 236 | Tasks => $self->ReadString(1), 237 | Languages => $self->ReadString(1), 238 | Check => $self->ReadString(1), 239 | MinVersion => { 240 | WinVersion => $self->ReadCardinal(), 241 | NTVersion => $self->ReadCardinal(), 242 | NTServicePack => $self->ReadWord(), 243 | }, 244 | OnlyBelowVersion => { 245 | WinVersion => $self->ReadCardinal(), 246 | NTVersion => $self->ReadCardinal(), 247 | NTServicePack => $self->ReadWord(), 248 | }, 249 | IconIndex => $self->ReadInteger(), 250 | ShowCmd => $self->ReadInteger(), 251 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 252 | HotKey => $self->ReadWord(), 253 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 254 | }; 255 | return $ret; 256 | } 257 | sub TSetupIniEntry { 258 | my ($self) = @_; 259 | my $ret; 260 | $ret = { 261 | Filename => $self->ReadString(1), 262 | Section => $self->ReadString(1), 263 | Entry => $self->ReadString(1), 264 | Value => $self->ReadString(1), 265 | Components => $self->ReadString(1), 266 | Tasks => $self->ReadString(1), 267 | Languages => $self->ReadString(1), 268 | Check => $self->ReadString(1), 269 | MinVersion => { 270 | WinVersion => $self->ReadCardinal(), 271 | NTVersion => $self->ReadCardinal(), 272 | NTServicePack => $self->ReadWord(), 273 | }, 274 | OnlyBelowVersion => { 275 | WinVersion => $self->ReadCardinal(), 276 | NTVersion => $self->ReadCardinal(), 277 | NTServicePack => $self->ReadWord(), 278 | }, 279 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 280 | }; 281 | return $ret; 282 | } 283 | sub TSetupRegistryEntry { 284 | my ($self) = @_; 285 | my $ret; 286 | $ret = { 287 | Subkey => $self->ReadString(1), 288 | ValueName => $self->ReadString(1), 289 | ValueData => $self->ReadString(1), 290 | Components => $self->ReadString(1), 291 | Tasks => $self->ReadString(1), 292 | Languages => $self->ReadString(1), 293 | Check => $self->ReadString(1), 294 | MinVersion => { 295 | WinVersion => $self->ReadCardinal(), 296 | NTVersion => $self->ReadCardinal(), 297 | NTServicePack => $self->ReadWord(), 298 | }, 299 | OnlyBelowVersion => { 300 | WinVersion => $self->ReadCardinal(), 301 | NTVersion => $self->ReadCardinal(), 302 | NTServicePack => $self->ReadWord(), 303 | }, 304 | RootKey => $self->HKEY(), 305 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 306 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 307 | }; 308 | return $ret; 309 | } 310 | sub TSetupDeleteEntry { 311 | my ($self) = @_; 312 | my $ret; 313 | $ret = { 314 | Name => $self->ReadString(1), 315 | Components => $self->ReadString(1), 316 | Tasks => $self->ReadString(1), 317 | Languages => $self->ReadString(1), 318 | Check => $self->ReadString(1), 319 | MinVersion => { 320 | WinVersion => $self->ReadCardinal(), 321 | NTVersion => $self->ReadCardinal(), 322 | NTServicePack => $self->ReadWord(), 323 | }, 324 | OnlyBelowVersion => { 325 | WinVersion => $self->ReadCardinal(), 326 | NTVersion => $self->ReadCardinal(), 327 | NTServicePack => $self->ReadWord(), 328 | }, 329 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 330 | }; 331 | return $ret; 332 | } 333 | sub TSetupRunEntry { 334 | my ($self) = @_; 335 | my $ret; 336 | $ret = { 337 | Name => $self->ReadString(1), 338 | Parameters => $self->ReadString(1), 339 | WorkingDir => $self->ReadString(1), 340 | RunOnceId => $self->ReadString(1), 341 | StatusMsg => $self->ReadString(1), 342 | Description => $self->ReadString(1), 343 | Components => $self->ReadString(1), 344 | Tasks => $self->ReadString(1), 345 | Languages => $self->ReadString(1), 346 | Check => $self->ReadString(1), 347 | MinVersion => { 348 | WinVersion => $self->ReadCardinal(), 349 | NTVersion => $self->ReadCardinal(), 350 | NTServicePack => $self->ReadWord(), 351 | }, 352 | OnlyBelowVersion => { 353 | WinVersion => $self->ReadCardinal(), 354 | NTVersion => $self->ReadCardinal(), 355 | NTServicePack => $self->ReadWord(), 356 | }, 357 | ShowCmd => $self->ReadInteger(), 358 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 359 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 360 | }; 361 | return $ret; 362 | } 363 | sub TSetupFileLocationEntry { 364 | my ($self) = @_; 365 | my $ret; 366 | $ret = { 367 | FirstSlice => $self->ReadInteger(), 368 | LastSlice => $self->ReadInteger(), 369 | StartOffset => $self->ReadLongInt(), 370 | ChunkSuboffset => $self->ReadInt64(), 371 | OriginalSize => $self->ReadInt64(), 372 | ChunkCompressedSize => $self->ReadInt64(), 373 | CRC => $self->ReadLongInt(), 374 | Date => $self->TFileTime(), 375 | FileVersionMS => $self->DWORD(), 376 | FileVersionLS => $self->DWORD(), 377 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid' ]), 378 | }; 379 | return $ret; 380 | } 381 | 1; 382 | -------------------------------------------------------------------------------- /Setup/Inno/Struct4009.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct4009; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | BaseFilename => $self->ReadString(1), 20 | LicenseText => $self->ReadString(1), 21 | InfoBeforeText => $self->ReadString(1), 22 | InfoAfterText => $self->ReadString(1), 23 | UninstallFilesDir => $self->ReadString(1), 24 | UninstallDisplayName => $self->ReadString(1), 25 | UninstallDisplayIcon => $self->ReadString(1), 26 | AppMutex => $self->ReadString(1), 27 | DefaultUserInfoName => $self->ReadString(1), 28 | DefaultUserInfoOrg => $self->ReadString(1), 29 | DefaultUserInfoSerial => $self->ReadString(1), 30 | CompiledCodeText => $self->ReadString(1), 31 | LeadBytes => $self->ReadSet(256), 32 | NumLanguageEntries => $self->ReadInteger(), 33 | NumTypeEntries => $self->ReadInteger(), 34 | NumComponentEntries => $self->ReadInteger(), 35 | NumTaskEntries => $self->ReadInteger(), 36 | NumDirEntries => $self->ReadInteger(), 37 | NumFileEntries => $self->ReadInteger(), 38 | NumFileLocationEntries => $self->ReadInteger(), 39 | NumIconEntries => $self->ReadInteger(), 40 | NumIniEntries => $self->ReadInteger(), 41 | NumRegistryEntries => $self->ReadInteger(), 42 | NumInstallDeleteEntries => $self->ReadInteger(), 43 | NumUninstallDeleteEntries => $self->ReadInteger(), 44 | NumRunEntries => $self->ReadInteger(), 45 | NumUninstallRunEntries => $self->ReadInteger(), 46 | MinVersion => { 47 | WinVersion => $self->ReadCardinal(), 48 | NTVersion => $self->ReadCardinal(), 49 | NTServicePack => $self->ReadWord(), 50 | }, 51 | OnlyBelowVersion => { 52 | WinVersion => $self->ReadCardinal(), 53 | NTVersion => $self->ReadCardinal(), 54 | NTServicePack => $self->ReadWord(), 55 | }, 56 | BackColor => $self->ReadLongInt(), 57 | BackColor2 => $self->ReadLongInt(), 58 | WizardImageBackColor => $self->ReadLongInt(), 59 | WizardSmallImageBackColor => $self->ReadLongInt(), 60 | Password => $self->ReadLongInt(), 61 | ExtraDiskSpaceRequired => $self->ReadInt64(), 62 | SlicesPerDisk => $self->ReadInteger(), 63 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 64 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 65 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 66 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 67 | PrivilegesRequired => $self->ReadEnum([ 'prNone', 'prPowerUser', 'prAdmin' ]), 68 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath', 'shUserInfoPage', 'shUsePreviousUserInfo', 'shUninstallRestartComputer', 'shRestartIfNeededByRun', 'shShowTasksTreeLines', 'shShowLanguageDialog', 'shDetectLanguageUsingLocale', 'shAllowCancelDuringInstall' ]), 69 | }; 70 | return $ret; 71 | } 72 | sub TSetupLanguageEntry { 73 | my ($self) = @_; 74 | my $ret; 75 | $ret = { 76 | Name => $self->ReadString(1), 77 | LanguageName => $self->ReadString(1), 78 | DialogFontName => $self->ReadString(1), 79 | TitleFontName => $self->ReadString(1), 80 | WelcomeFontName => $self->ReadString(1), 81 | CopyrightFontName => $self->ReadString(1), 82 | Data => $self->ReadString(1), 83 | LicenseText => $self->ReadString(1), 84 | InfoBeforeText => $self->ReadString(1), 85 | InfoAfterText => $self->ReadString(1), 86 | LanguageID => $self->ReadCardinal(), 87 | DialogFontSize => $self->ReadInteger(), 88 | DialogFontStandardHeight => $self->ReadInteger(), 89 | TitleFontSize => $self->ReadInteger(), 90 | WelcomeFontSize => $self->ReadInteger(), 91 | CopyrightFontSize => $self->ReadInteger(), 92 | }; 93 | return $ret; 94 | } 95 | sub TSetupTypeEntry { 96 | my ($self) = @_; 97 | my $ret; 98 | $ret = { 99 | Name => $self->ReadString(1), 100 | Description => $self->ReadString(1), 101 | Languages => $self->ReadString(1), 102 | Check => $self->ReadString(1), 103 | MinVersion => { 104 | WinVersion => $self->ReadCardinal(), 105 | NTVersion => $self->ReadCardinal(), 106 | NTServicePack => $self->ReadWord(), 107 | }, 108 | OnlyBelowVersion => { 109 | WinVersion => $self->ReadCardinal(), 110 | NTVersion => $self->ReadCardinal(), 111 | NTServicePack => $self->ReadWord(), 112 | }, 113 | Options => $self->ReadSet([ 'toIsCustom' ]), 114 | Typ => $self->ReadEnum([ 'ttUser', 'ttDefaultFull', 'ttDefaultCompact', 'ttDefaultCustom' ]), 115 | Size => $self->ReadInt64(), 116 | }; 117 | return $ret; 118 | } 119 | sub TSetupComponentEntry { 120 | my ($self) = @_; 121 | my $ret; 122 | $ret = { 123 | Name => $self->ReadString(1), 124 | Description => $self->ReadString(1), 125 | Types => $self->ReadString(1), 126 | Languages => $self->ReadString(1), 127 | Check => $self->ReadString(1), 128 | ExtraDiskSpaceRequired => $self->ReadInt64(), 129 | Level => $self->ReadInteger(), 130 | Used => $self->ReadByte(), 131 | MinVersion => { 132 | WinVersion => $self->ReadCardinal(), 133 | NTVersion => $self->ReadCardinal(), 134 | NTServicePack => $self->ReadWord(), 135 | }, 136 | OnlyBelowVersion => { 137 | WinVersion => $self->ReadCardinal(), 138 | NTVersion => $self->ReadCardinal(), 139 | NTServicePack => $self->ReadWord(), 140 | }, 141 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning', 'coExclusive' ]), 142 | Size => $self->ReadInt64(), 143 | }; 144 | return $ret; 145 | } 146 | sub TSetupTaskEntry { 147 | my ($self) = @_; 148 | my $ret; 149 | $ret = { 150 | Name => $self->ReadString(1), 151 | Description => $self->ReadString(1), 152 | GroupDescription => $self->ReadString(1), 153 | Components => $self->ReadString(1), 154 | Languages => $self->ReadString(1), 155 | Check => $self->ReadString(1), 156 | Level => $self->ReadInteger(), 157 | Used => $self->ReadByte(), 158 | MinVersion => { 159 | WinVersion => $self->ReadCardinal(), 160 | NTVersion => $self->ReadCardinal(), 161 | NTServicePack => $self->ReadWord(), 162 | }, 163 | OnlyBelowVersion => { 164 | WinVersion => $self->ReadCardinal(), 165 | NTVersion => $self->ReadCardinal(), 166 | NTServicePack => $self->ReadWord(), 167 | }, 168 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 169 | }; 170 | return $ret; 171 | } 172 | sub TSetupDirEntry { 173 | my ($self) = @_; 174 | my $ret; 175 | $ret = { 176 | DirName => $self->ReadString(1), 177 | Components => $self->ReadString(1), 178 | Tasks => $self->ReadString(1), 179 | Languages => $self->ReadString(1), 180 | Check => $self->ReadString(1), 181 | Attribs => $self->ReadInteger(), 182 | MinVersion => { 183 | WinVersion => $self->ReadCardinal(), 184 | NTVersion => $self->ReadCardinal(), 185 | NTServicePack => $self->ReadWord(), 186 | }, 187 | OnlyBelowVersion => { 188 | WinVersion => $self->ReadCardinal(), 189 | NTVersion => $self->ReadCardinal(), 190 | NTServicePack => $self->ReadWord(), 191 | }, 192 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 193 | }; 194 | return $ret; 195 | } 196 | sub TSetupFileEntry { 197 | my ($self) = @_; 198 | my $ret; 199 | $ret = { 200 | SourceFilename => $self->ReadString(1), 201 | DestName => $self->ReadString(1), 202 | InstallFontName => $self->ReadString(1), 203 | Components => $self->ReadString(1), 204 | Tasks => $self->ReadString(1), 205 | Languages => $self->ReadString(1), 206 | Check => $self->ReadString(1), 207 | MinVersion => { 208 | WinVersion => $self->ReadCardinal(), 209 | NTVersion => $self->ReadCardinal(), 210 | NTServicePack => $self->ReadWord(), 211 | }, 212 | OnlyBelowVersion => { 213 | WinVersion => $self->ReadCardinal(), 214 | NTVersion => $self->ReadCardinal(), 215 | NTServicePack => $self->ReadWord(), 216 | }, 217 | LocationEntry => $self->ReadInteger(), 218 | Attribs => $self->ReadInteger(), 219 | ExternalSize => $self->ReadInt64(), 220 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStamp', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError', 'foUninsRestartDelete', 'foOnlyIfDoesntExist', 'foIgnoreVersion', 'foPromptIfOlder', 'foDontCopy', 'foUninsRemoveReadOnly' ]), 221 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 222 | }; 223 | return $ret; 224 | } 225 | sub TSetupIconEntry { 226 | my ($self) = @_; 227 | my $ret; 228 | $ret = { 229 | IconName => $self->ReadString(1), 230 | Filename => $self->ReadString(1), 231 | Parameters => $self->ReadString(1), 232 | WorkingDir => $self->ReadString(1), 233 | IconFilename => $self->ReadString(1), 234 | Comment => $self->ReadString(1), 235 | Components => $self->ReadString(1), 236 | Tasks => $self->ReadString(1), 237 | Languages => $self->ReadString(1), 238 | Check => $self->ReadString(1), 239 | MinVersion => { 240 | WinVersion => $self->ReadCardinal(), 241 | NTVersion => $self->ReadCardinal(), 242 | NTServicePack => $self->ReadWord(), 243 | }, 244 | OnlyBelowVersion => { 245 | WinVersion => $self->ReadCardinal(), 246 | NTVersion => $self->ReadCardinal(), 247 | NTServicePack => $self->ReadWord(), 248 | }, 249 | IconIndex => $self->ReadInteger(), 250 | ShowCmd => $self->ReadInteger(), 251 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 252 | HotKey => $self->ReadWord(), 253 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 254 | }; 255 | return $ret; 256 | } 257 | sub TSetupIniEntry { 258 | my ($self) = @_; 259 | my $ret; 260 | $ret = { 261 | Filename => $self->ReadString(1), 262 | Section => $self->ReadString(1), 263 | Entry => $self->ReadString(1), 264 | Value => $self->ReadString(1), 265 | Components => $self->ReadString(1), 266 | Tasks => $self->ReadString(1), 267 | Languages => $self->ReadString(1), 268 | Check => $self->ReadString(1), 269 | MinVersion => { 270 | WinVersion => $self->ReadCardinal(), 271 | NTVersion => $self->ReadCardinal(), 272 | NTServicePack => $self->ReadWord(), 273 | }, 274 | OnlyBelowVersion => { 275 | WinVersion => $self->ReadCardinal(), 276 | NTVersion => $self->ReadCardinal(), 277 | NTServicePack => $self->ReadWord(), 278 | }, 279 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 280 | }; 281 | return $ret; 282 | } 283 | sub TSetupRegistryEntry { 284 | my ($self) = @_; 285 | my $ret; 286 | $ret = { 287 | Subkey => $self->ReadString(1), 288 | ValueName => $self->ReadString(1), 289 | ValueData => $self->ReadString(1), 290 | Components => $self->ReadString(1), 291 | Tasks => $self->ReadString(1), 292 | Languages => $self->ReadString(1), 293 | Check => $self->ReadString(1), 294 | MinVersion => { 295 | WinVersion => $self->ReadCardinal(), 296 | NTVersion => $self->ReadCardinal(), 297 | NTServicePack => $self->ReadWord(), 298 | }, 299 | OnlyBelowVersion => { 300 | WinVersion => $self->ReadCardinal(), 301 | NTVersion => $self->ReadCardinal(), 302 | NTServicePack => $self->ReadWord(), 303 | }, 304 | RootKey => $self->HKEY(), 305 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 306 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 307 | }; 308 | return $ret; 309 | } 310 | sub TSetupDeleteEntry { 311 | my ($self) = @_; 312 | my $ret; 313 | $ret = { 314 | Name => $self->ReadString(1), 315 | Components => $self->ReadString(1), 316 | Tasks => $self->ReadString(1), 317 | Languages => $self->ReadString(1), 318 | Check => $self->ReadString(1), 319 | MinVersion => { 320 | WinVersion => $self->ReadCardinal(), 321 | NTVersion => $self->ReadCardinal(), 322 | NTServicePack => $self->ReadWord(), 323 | }, 324 | OnlyBelowVersion => { 325 | WinVersion => $self->ReadCardinal(), 326 | NTVersion => $self->ReadCardinal(), 327 | NTServicePack => $self->ReadWord(), 328 | }, 329 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 330 | }; 331 | return $ret; 332 | } 333 | sub TSetupRunEntry { 334 | my ($self) = @_; 335 | my $ret; 336 | $ret = { 337 | Name => $self->ReadString(1), 338 | Parameters => $self->ReadString(1), 339 | WorkingDir => $self->ReadString(1), 340 | RunOnceId => $self->ReadString(1), 341 | StatusMsg => $self->ReadString(1), 342 | Description => $self->ReadString(1), 343 | Components => $self->ReadString(1), 344 | Tasks => $self->ReadString(1), 345 | Languages => $self->ReadString(1), 346 | Check => $self->ReadString(1), 347 | MinVersion => { 348 | WinVersion => $self->ReadCardinal(), 349 | NTVersion => $self->ReadCardinal(), 350 | NTServicePack => $self->ReadWord(), 351 | }, 352 | OnlyBelowVersion => { 353 | WinVersion => $self->ReadCardinal(), 354 | NTVersion => $self->ReadCardinal(), 355 | NTServicePack => $self->ReadWord(), 356 | }, 357 | ShowCmd => $self->ReadInteger(), 358 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 359 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 360 | }; 361 | return $ret; 362 | } 363 | sub TSetupFileLocationEntry { 364 | my ($self) = @_; 365 | my $ret; 366 | $ret = { 367 | FirstSlice => $self->ReadInteger(), 368 | LastSlice => $self->ReadInteger(), 369 | StartOffset => $self->ReadLongInt(), 370 | ChunkSuboffset => $self->ReadInt64(), 371 | OriginalSize => $self->ReadInt64(), 372 | ChunkCompressedSize => $self->ReadInt64(), 373 | CRC => $self->ReadLongInt(), 374 | Date => $self->TFileTime(), 375 | FileVersionMS => $self->DWORD(), 376 | FileVersionLS => $self->DWORD(), 377 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid' ]), 378 | }; 379 | return $ret; 380 | } 381 | 1; 382 | -------------------------------------------------------------------------------- /Setup/Inno/Struct4010.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct4010; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | BaseFilename => $self->ReadString(1), 20 | LicenseText => $self->ReadString(1), 21 | InfoBeforeText => $self->ReadString(1), 22 | InfoAfterText => $self->ReadString(1), 23 | UninstallFilesDir => $self->ReadString(1), 24 | UninstallDisplayName => $self->ReadString(1), 25 | UninstallDisplayIcon => $self->ReadString(1), 26 | AppMutex => $self->ReadString(1), 27 | DefaultUserInfoName => $self->ReadString(1), 28 | DefaultUserInfoOrg => $self->ReadString(1), 29 | DefaultUserInfoSerial => $self->ReadString(1), 30 | CompiledCodeText => $self->ReadString(1), 31 | LeadBytes => $self->ReadSet(256), 32 | NumLanguageEntries => $self->ReadInteger(), 33 | NumTypeEntries => $self->ReadInteger(), 34 | NumComponentEntries => $self->ReadInteger(), 35 | NumTaskEntries => $self->ReadInteger(), 36 | NumDirEntries => $self->ReadInteger(), 37 | NumFileEntries => $self->ReadInteger(), 38 | NumFileLocationEntries => $self->ReadInteger(), 39 | NumIconEntries => $self->ReadInteger(), 40 | NumIniEntries => $self->ReadInteger(), 41 | NumRegistryEntries => $self->ReadInteger(), 42 | NumInstallDeleteEntries => $self->ReadInteger(), 43 | NumUninstallDeleteEntries => $self->ReadInteger(), 44 | NumRunEntries => $self->ReadInteger(), 45 | NumUninstallRunEntries => $self->ReadInteger(), 46 | MinVersion => { 47 | WinVersion => $self->ReadCardinal(), 48 | NTVersion => $self->ReadCardinal(), 49 | NTServicePack => $self->ReadWord(), 50 | }, 51 | OnlyBelowVersion => { 52 | WinVersion => $self->ReadCardinal(), 53 | NTVersion => $self->ReadCardinal(), 54 | NTServicePack => $self->ReadWord(), 55 | }, 56 | BackColor => $self->ReadLongInt(), 57 | BackColor2 => $self->ReadLongInt(), 58 | WizardImageBackColor => $self->ReadLongInt(), 59 | WizardSmallImageBackColor => $self->ReadLongInt(), 60 | Password => $self->ReadLongInt(), 61 | ExtraDiskSpaceRequired => $self->ReadInt64(), 62 | SlicesPerDisk => $self->ReadInteger(), 63 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 64 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 65 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 66 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 67 | PrivilegesRequired => $self->ReadEnum([ 'prNone', 'prPowerUser', 'prAdmin' ]), 68 | ShowLanguageDialog => $self->ReadEnum([ 'slYes', 'slNo', 'slAuto' ]), 69 | LanguageDetectionMethod => $self->ReadEnum([ 'ldUILanguage', 'ldLocale', 'ldNone' ]), 70 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath', 'shUserInfoPage', 'shUsePreviousUserInfo', 'shUninstallRestartComputer', 'shRestartIfNeededByRun', 'shShowTasksTreeLines', 'shAllowCancelDuringInstall' ]), 71 | }; 72 | return $ret; 73 | } 74 | sub TSetupLanguageEntry { 75 | my ($self) = @_; 76 | my $ret; 77 | $ret = { 78 | Name => $self->ReadString(1), 79 | LanguageName => $self->ReadString(1), 80 | DialogFontName => $self->ReadString(1), 81 | TitleFontName => $self->ReadString(1), 82 | WelcomeFontName => $self->ReadString(1), 83 | CopyrightFontName => $self->ReadString(1), 84 | Data => $self->ReadString(1), 85 | LicenseText => $self->ReadString(1), 86 | InfoBeforeText => $self->ReadString(1), 87 | InfoAfterText => $self->ReadString(1), 88 | LanguageID => $self->ReadCardinal(), 89 | DialogFontSize => $self->ReadInteger(), 90 | DialogFontStandardHeight => $self->ReadInteger(), 91 | TitleFontSize => $self->ReadInteger(), 92 | WelcomeFontSize => $self->ReadInteger(), 93 | CopyrightFontSize => $self->ReadInteger(), 94 | }; 95 | return $ret; 96 | } 97 | sub TSetupTypeEntry { 98 | my ($self) = @_; 99 | my $ret; 100 | $ret = { 101 | Name => $self->ReadString(1), 102 | Description => $self->ReadString(1), 103 | Languages => $self->ReadString(1), 104 | Check => $self->ReadString(1), 105 | MinVersion => { 106 | WinVersion => $self->ReadCardinal(), 107 | NTVersion => $self->ReadCardinal(), 108 | NTServicePack => $self->ReadWord(), 109 | }, 110 | OnlyBelowVersion => { 111 | WinVersion => $self->ReadCardinal(), 112 | NTVersion => $self->ReadCardinal(), 113 | NTServicePack => $self->ReadWord(), 114 | }, 115 | Options => $self->ReadSet([ 'toIsCustom' ]), 116 | Typ => $self->ReadEnum([ 'ttUser', 'ttDefaultFull', 'ttDefaultCompact', 'ttDefaultCustom' ]), 117 | Size => $self->ReadInt64(), 118 | }; 119 | return $ret; 120 | } 121 | sub TSetupComponentEntry { 122 | my ($self) = @_; 123 | my $ret; 124 | $ret = { 125 | Name => $self->ReadString(1), 126 | Description => $self->ReadString(1), 127 | Types => $self->ReadString(1), 128 | Languages => $self->ReadString(1), 129 | Check => $self->ReadString(1), 130 | ExtraDiskSpaceRequired => $self->ReadInt64(), 131 | Level => $self->ReadInteger(), 132 | Used => $self->ReadByte(), 133 | MinVersion => { 134 | WinVersion => $self->ReadCardinal(), 135 | NTVersion => $self->ReadCardinal(), 136 | NTServicePack => $self->ReadWord(), 137 | }, 138 | OnlyBelowVersion => { 139 | WinVersion => $self->ReadCardinal(), 140 | NTVersion => $self->ReadCardinal(), 141 | NTServicePack => $self->ReadWord(), 142 | }, 143 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning', 'coExclusive' ]), 144 | Size => $self->ReadInt64(), 145 | }; 146 | return $ret; 147 | } 148 | sub TSetupTaskEntry { 149 | my ($self) = @_; 150 | my $ret; 151 | $ret = { 152 | Name => $self->ReadString(1), 153 | Description => $self->ReadString(1), 154 | GroupDescription => $self->ReadString(1), 155 | Components => $self->ReadString(1), 156 | Languages => $self->ReadString(1), 157 | Check => $self->ReadString(1), 158 | Level => $self->ReadInteger(), 159 | Used => $self->ReadByte(), 160 | MinVersion => { 161 | WinVersion => $self->ReadCardinal(), 162 | NTVersion => $self->ReadCardinal(), 163 | NTServicePack => $self->ReadWord(), 164 | }, 165 | OnlyBelowVersion => { 166 | WinVersion => $self->ReadCardinal(), 167 | NTVersion => $self->ReadCardinal(), 168 | NTServicePack => $self->ReadWord(), 169 | }, 170 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 171 | }; 172 | return $ret; 173 | } 174 | sub TSetupDirEntry { 175 | my ($self) = @_; 176 | my $ret; 177 | $ret = { 178 | DirName => $self->ReadString(1), 179 | Components => $self->ReadString(1), 180 | Tasks => $self->ReadString(1), 181 | Languages => $self->ReadString(1), 182 | Check => $self->ReadString(1), 183 | Attribs => $self->ReadInteger(), 184 | MinVersion => { 185 | WinVersion => $self->ReadCardinal(), 186 | NTVersion => $self->ReadCardinal(), 187 | NTServicePack => $self->ReadWord(), 188 | }, 189 | OnlyBelowVersion => { 190 | WinVersion => $self->ReadCardinal(), 191 | NTVersion => $self->ReadCardinal(), 192 | NTServicePack => $self->ReadWord(), 193 | }, 194 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 195 | }; 196 | return $ret; 197 | } 198 | sub TSetupFileEntry { 199 | my ($self) = @_; 200 | my $ret; 201 | $ret = { 202 | SourceFilename => $self->ReadString(1), 203 | DestName => $self->ReadString(1), 204 | InstallFontName => $self->ReadString(1), 205 | Components => $self->ReadString(1), 206 | Tasks => $self->ReadString(1), 207 | Languages => $self->ReadString(1), 208 | Check => $self->ReadString(1), 209 | MinVersion => { 210 | WinVersion => $self->ReadCardinal(), 211 | NTVersion => $self->ReadCardinal(), 212 | NTServicePack => $self->ReadWord(), 213 | }, 214 | OnlyBelowVersion => { 215 | WinVersion => $self->ReadCardinal(), 216 | NTVersion => $self->ReadCardinal(), 217 | NTServicePack => $self->ReadWord(), 218 | }, 219 | LocationEntry => $self->ReadInteger(), 220 | Attribs => $self->ReadInteger(), 221 | ExternalSize => $self->ReadInt64(), 222 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStamp', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError', 'foUninsRestartDelete', 'foOnlyIfDoesntExist', 'foIgnoreVersion', 'foPromptIfOlder', 'foDontCopy', 'foUninsRemoveReadOnly' ]), 223 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 224 | }; 225 | return $ret; 226 | } 227 | sub TSetupIconEntry { 228 | my ($self) = @_; 229 | my $ret; 230 | $ret = { 231 | IconName => $self->ReadString(1), 232 | Filename => $self->ReadString(1), 233 | Parameters => $self->ReadString(1), 234 | WorkingDir => $self->ReadString(1), 235 | IconFilename => $self->ReadString(1), 236 | Comment => $self->ReadString(1), 237 | Components => $self->ReadString(1), 238 | Tasks => $self->ReadString(1), 239 | Languages => $self->ReadString(1), 240 | Check => $self->ReadString(1), 241 | MinVersion => { 242 | WinVersion => $self->ReadCardinal(), 243 | NTVersion => $self->ReadCardinal(), 244 | NTServicePack => $self->ReadWord(), 245 | }, 246 | OnlyBelowVersion => { 247 | WinVersion => $self->ReadCardinal(), 248 | NTVersion => $self->ReadCardinal(), 249 | NTServicePack => $self->ReadWord(), 250 | }, 251 | IconIndex => $self->ReadInteger(), 252 | ShowCmd => $self->ReadInteger(), 253 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 254 | HotKey => $self->ReadWord(), 255 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 256 | }; 257 | return $ret; 258 | } 259 | sub TSetupIniEntry { 260 | my ($self) = @_; 261 | my $ret; 262 | $ret = { 263 | Filename => $self->ReadString(1), 264 | Section => $self->ReadString(1), 265 | Entry => $self->ReadString(1), 266 | Value => $self->ReadString(1), 267 | Components => $self->ReadString(1), 268 | Tasks => $self->ReadString(1), 269 | Languages => $self->ReadString(1), 270 | Check => $self->ReadString(1), 271 | MinVersion => { 272 | WinVersion => $self->ReadCardinal(), 273 | NTVersion => $self->ReadCardinal(), 274 | NTServicePack => $self->ReadWord(), 275 | }, 276 | OnlyBelowVersion => { 277 | WinVersion => $self->ReadCardinal(), 278 | NTVersion => $self->ReadCardinal(), 279 | NTServicePack => $self->ReadWord(), 280 | }, 281 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 282 | }; 283 | return $ret; 284 | } 285 | sub TSetupRegistryEntry { 286 | my ($self) = @_; 287 | my $ret; 288 | $ret = { 289 | Subkey => $self->ReadString(1), 290 | ValueName => $self->ReadString(1), 291 | ValueData => $self->ReadString(1), 292 | Components => $self->ReadString(1), 293 | Tasks => $self->ReadString(1), 294 | Languages => $self->ReadString(1), 295 | Check => $self->ReadString(1), 296 | MinVersion => { 297 | WinVersion => $self->ReadCardinal(), 298 | NTVersion => $self->ReadCardinal(), 299 | NTServicePack => $self->ReadWord(), 300 | }, 301 | OnlyBelowVersion => { 302 | WinVersion => $self->ReadCardinal(), 303 | NTVersion => $self->ReadCardinal(), 304 | NTServicePack => $self->ReadWord(), 305 | }, 306 | RootKey => $self->HKEY(), 307 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 308 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 309 | }; 310 | return $ret; 311 | } 312 | sub TSetupDeleteEntry { 313 | my ($self) = @_; 314 | my $ret; 315 | $ret = { 316 | Name => $self->ReadString(1), 317 | Components => $self->ReadString(1), 318 | Tasks => $self->ReadString(1), 319 | Languages => $self->ReadString(1), 320 | Check => $self->ReadString(1), 321 | MinVersion => { 322 | WinVersion => $self->ReadCardinal(), 323 | NTVersion => $self->ReadCardinal(), 324 | NTServicePack => $self->ReadWord(), 325 | }, 326 | OnlyBelowVersion => { 327 | WinVersion => $self->ReadCardinal(), 328 | NTVersion => $self->ReadCardinal(), 329 | NTServicePack => $self->ReadWord(), 330 | }, 331 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 332 | }; 333 | return $ret; 334 | } 335 | sub TSetupRunEntry { 336 | my ($self) = @_; 337 | my $ret; 338 | $ret = { 339 | Name => $self->ReadString(1), 340 | Parameters => $self->ReadString(1), 341 | WorkingDir => $self->ReadString(1), 342 | RunOnceId => $self->ReadString(1), 343 | StatusMsg => $self->ReadString(1), 344 | Description => $self->ReadString(1), 345 | Components => $self->ReadString(1), 346 | Tasks => $self->ReadString(1), 347 | Languages => $self->ReadString(1), 348 | Check => $self->ReadString(1), 349 | MinVersion => { 350 | WinVersion => $self->ReadCardinal(), 351 | NTVersion => $self->ReadCardinal(), 352 | NTServicePack => $self->ReadWord(), 353 | }, 354 | OnlyBelowVersion => { 355 | WinVersion => $self->ReadCardinal(), 356 | NTVersion => $self->ReadCardinal(), 357 | NTServicePack => $self->ReadWord(), 358 | }, 359 | ShowCmd => $self->ReadInteger(), 360 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 361 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 362 | }; 363 | return $ret; 364 | } 365 | sub TSetupFileLocationEntry { 366 | my ($self) = @_; 367 | my $ret; 368 | $ret = { 369 | FirstSlice => $self->ReadInteger(), 370 | LastSlice => $self->ReadInteger(), 371 | StartOffset => $self->ReadLongInt(), 372 | ChunkSuboffset => $self->ReadInt64(), 373 | OriginalSize => $self->ReadInt64(), 374 | ChunkCompressedSize => $self->ReadInt64(), 375 | CRC => $self->ReadLongInt(), 376 | TimeStamp => $self->TFileTime(), 377 | FileVersionMS => $self->DWORD(), 378 | FileVersionLS => $self->DWORD(), 379 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid', 'foTimeStampInUTC' ]), 380 | }; 381 | return $ret; 382 | } 383 | 1; 384 | -------------------------------------------------------------------------------- /Setup/Inno/Struct4011.pm: -------------------------------------------------------------------------------- 1 | package Setup::Inno::Struct4011; 2 | use strict; 3 | use base 'Setup::Inno::Struct'; 4 | sub TSetupHeader { 5 | my ($self) = @_; 6 | my $ret; 7 | $ret = { 8 | AppName => $self->ReadString(1), 9 | AppVerName => $self->ReadString(1), 10 | AppId => $self->ReadString(1), 11 | AppCopyright => $self->ReadString(1), 12 | AppPublisher => $self->ReadString(1), 13 | AppPublisherURL => $self->ReadString(1), 14 | AppSupportURL => $self->ReadString(1), 15 | AppUpdatesURL => $self->ReadString(1), 16 | AppVersion => $self->ReadString(1), 17 | DefaultDirName => $self->ReadString(1), 18 | DefaultGroupName => $self->ReadString(1), 19 | BaseFilename => $self->ReadString(1), 20 | LicenseText => $self->ReadString(1), 21 | InfoBeforeText => $self->ReadString(1), 22 | InfoAfterText => $self->ReadString(1), 23 | UninstallFilesDir => $self->ReadString(1), 24 | UninstallDisplayName => $self->ReadString(1), 25 | UninstallDisplayIcon => $self->ReadString(1), 26 | AppMutex => $self->ReadString(1), 27 | DefaultUserInfoName => $self->ReadString(1), 28 | DefaultUserInfoOrg => $self->ReadString(1), 29 | DefaultUserInfoSerial => $self->ReadString(1), 30 | CompiledCodeText => $self->ReadString(1), 31 | LeadBytes => $self->ReadSet(256), 32 | NumLanguageEntries => $self->ReadInteger(), 33 | NumTypeEntries => $self->ReadInteger(), 34 | NumComponentEntries => $self->ReadInteger(), 35 | NumTaskEntries => $self->ReadInteger(), 36 | NumDirEntries => $self->ReadInteger(), 37 | NumFileEntries => $self->ReadInteger(), 38 | NumFileLocationEntries => $self->ReadInteger(), 39 | NumIconEntries => $self->ReadInteger(), 40 | NumIniEntries => $self->ReadInteger(), 41 | NumRegistryEntries => $self->ReadInteger(), 42 | NumInstallDeleteEntries => $self->ReadInteger(), 43 | NumUninstallDeleteEntries => $self->ReadInteger(), 44 | NumRunEntries => $self->ReadInteger(), 45 | NumUninstallRunEntries => $self->ReadInteger(), 46 | MinVersion => { 47 | WinVersion => $self->ReadCardinal(), 48 | NTVersion => $self->ReadCardinal(), 49 | NTServicePack => $self->ReadWord(), 50 | }, 51 | OnlyBelowVersion => { 52 | WinVersion => $self->ReadCardinal(), 53 | NTVersion => $self->ReadCardinal(), 54 | NTServicePack => $self->ReadWord(), 55 | }, 56 | BackColor => $self->ReadLongInt(), 57 | BackColor2 => $self->ReadLongInt(), 58 | WizardImageBackColor => $self->ReadLongInt(), 59 | WizardSmallImageBackColor => $self->ReadLongInt(), 60 | Password => $self->ReadLongInt(), 61 | ExtraDiskSpaceRequired => $self->ReadInt64(), 62 | SlicesPerDisk => $self->ReadInteger(), 63 | InstallMode => $self->ReadEnum([ 'imNormal', 'imSilent', 'imVerySilent' ]), 64 | UninstallLogMode => $self->ReadEnum([ 'lmAppend', 'lmNew', 'lmOverwrite' ]), 65 | UninstallStyle => $self->ReadEnum([ 'usClassic', 'usModern' ]), 66 | DirExistsWarning => $self->ReadEnum([ 'ddAuto', 'ddNo', 'ddYes' ]), 67 | PrivilegesRequired => $self->ReadEnum([ 'prNone', 'prPowerUser', 'prAdmin' ]), 68 | ShowLanguageDialog => $self->ReadEnum([ 'slYes', 'slNo', 'slAuto' ]), 69 | LanguageDetectionMethod => $self->ReadEnum([ 'ldUILanguage', 'ldLocale', 'ldNone' ]), 70 | Options => $self->ReadSet([ 'shDisableStartupPrompt', 'shUninstallable', 'shCreateAppDir', 'shDisableDirPage', 'shDisableProgramGroupPage', 'shAllowNoIcons', 'shAlwaysRestart', 'shAlwaysUsePersonalGroup', 'shWindowVisible', 'shWindowShowCaption', 'shWindowResizable', 'shWindowStartMaximized', 'shEnableDirDoesntExistWarning', 'shDisableAppendDir', 'shPassword', 'shAllowRootDirectory', 'shDisableFinishedPage', 'shChangesAssociations', 'shCreateUninstallRegKey', 'shUsePreviousAppDir', 'shBackColorHorizontal', 'shUsePreviousGroup', 'shUpdateUninstallLogAppName', 'shUsePreviousSetupType', 'shDisableReadyMemo', 'shAlwaysShowComponentsList', 'shFlatComponentsList', 'shShowComponentSizes', 'shUsePreviousTasks', 'shDisableReadyPage', 'shAlwaysShowDirOnReadyPage', 'shAlwaysShowGroupOnReadyPage', 'shBzipUsed', 'shAllowUNCPath', 'shUserInfoPage', 'shUsePreviousUserInfo', 'shUninstallRestartComputer', 'shRestartIfNeededByRun', 'shShowTasksTreeLines', 'shAllowCancelDuringInstall' ]), 71 | }; 72 | return $ret; 73 | } 74 | sub TSetupLanguageEntry { 75 | my ($self) = @_; 76 | my $ret; 77 | $ret = { 78 | Name => $self->ReadString(1), 79 | LanguageName => $self->ReadString(1), 80 | DialogFontName => $self->ReadString(1), 81 | TitleFontName => $self->ReadString(1), 82 | WelcomeFontName => $self->ReadString(1), 83 | CopyrightFontName => $self->ReadString(1), 84 | Data => $self->ReadString(1), 85 | LicenseText => $self->ReadString(1), 86 | InfoBeforeText => $self->ReadString(1), 87 | InfoAfterText => $self->ReadString(1), 88 | LanguageID => $self->ReadCardinal(), 89 | DialogFontSize => $self->ReadInteger(), 90 | DialogFontStandardHeight => $self->ReadInteger(), 91 | TitleFontSize => $self->ReadInteger(), 92 | WelcomeFontSize => $self->ReadInteger(), 93 | CopyrightFontSize => $self->ReadInteger(), 94 | }; 95 | return $ret; 96 | } 97 | sub TSetupTypeEntry { 98 | my ($self) = @_; 99 | my $ret; 100 | $ret = { 101 | Name => $self->ReadString(1), 102 | Description => $self->ReadString(1), 103 | Languages => $self->ReadString(1), 104 | Check => $self->ReadString(1), 105 | MinVersion => { 106 | WinVersion => $self->ReadCardinal(), 107 | NTVersion => $self->ReadCardinal(), 108 | NTServicePack => $self->ReadWord(), 109 | }, 110 | OnlyBelowVersion => { 111 | WinVersion => $self->ReadCardinal(), 112 | NTVersion => $self->ReadCardinal(), 113 | NTServicePack => $self->ReadWord(), 114 | }, 115 | Options => $self->ReadSet([ 'toIsCustom' ]), 116 | Typ => $self->ReadEnum([ 'ttUser', 'ttDefaultFull', 'ttDefaultCompact', 'ttDefaultCustom' ]), 117 | Size => $self->ReadInt64(), 118 | }; 119 | return $ret; 120 | } 121 | sub TSetupComponentEntry { 122 | my ($self) = @_; 123 | my $ret; 124 | $ret = { 125 | Name => $self->ReadString(1), 126 | Description => $self->ReadString(1), 127 | Types => $self->ReadString(1), 128 | Languages => $self->ReadString(1), 129 | Check => $self->ReadString(1), 130 | ExtraDiskSpaceRequired => $self->ReadInt64(), 131 | Level => $self->ReadInteger(), 132 | Used => $self->ReadByte(), 133 | MinVersion => { 134 | WinVersion => $self->ReadCardinal(), 135 | NTVersion => $self->ReadCardinal(), 136 | NTServicePack => $self->ReadWord(), 137 | }, 138 | OnlyBelowVersion => { 139 | WinVersion => $self->ReadCardinal(), 140 | NTVersion => $self->ReadCardinal(), 141 | NTServicePack => $self->ReadWord(), 142 | }, 143 | Options => $self->ReadSet([ 'coFixed', 'coRestart', 'coDisableNoUninstallWarning', 'coExclusive' ]), 144 | Size => $self->ReadInt64(), 145 | }; 146 | return $ret; 147 | } 148 | sub TSetupTaskEntry { 149 | my ($self) = @_; 150 | my $ret; 151 | $ret = { 152 | Name => $self->ReadString(1), 153 | Description => $self->ReadString(1), 154 | GroupDescription => $self->ReadString(1), 155 | Components => $self->ReadString(1), 156 | Languages => $self->ReadString(1), 157 | Check => $self->ReadString(1), 158 | Level => $self->ReadInteger(), 159 | Used => $self->ReadByte(), 160 | MinVersion => { 161 | WinVersion => $self->ReadCardinal(), 162 | NTVersion => $self->ReadCardinal(), 163 | NTServicePack => $self->ReadWord(), 164 | }, 165 | OnlyBelowVersion => { 166 | WinVersion => $self->ReadCardinal(), 167 | NTVersion => $self->ReadCardinal(), 168 | NTServicePack => $self->ReadWord(), 169 | }, 170 | Options => $self->ReadSet([ 'toExclusive', 'toUnchecked', 'toRestart', 'toCheckedOnce' ]), 171 | }; 172 | return $ret; 173 | } 174 | sub TSetupDirEntry { 175 | my ($self) = @_; 176 | my $ret; 177 | $ret = { 178 | DirName => $self->ReadString(1), 179 | Components => $self->ReadString(1), 180 | Tasks => $self->ReadString(1), 181 | Languages => $self->ReadString(1), 182 | Check => $self->ReadString(1), 183 | Permissions => $self->ReadString(1), 184 | Attribs => $self->ReadInteger(), 185 | MinVersion => { 186 | WinVersion => $self->ReadCardinal(), 187 | NTVersion => $self->ReadCardinal(), 188 | NTServicePack => $self->ReadWord(), 189 | }, 190 | OnlyBelowVersion => { 191 | WinVersion => $self->ReadCardinal(), 192 | NTVersion => $self->ReadCardinal(), 193 | NTServicePack => $self->ReadWord(), 194 | }, 195 | Options => $self->ReadSet([ 'doUninsNeverUninstall', 'doDeleteAfterInstall', 'doUninsAlwaysUninstall' ]), 196 | }; 197 | return $ret; 198 | } 199 | sub TSetupFileEntry { 200 | my ($self) = @_; 201 | my $ret; 202 | $ret = { 203 | SourceFilename => $self->ReadString(1), 204 | DestName => $self->ReadString(1), 205 | InstallFontName => $self->ReadString(1), 206 | Components => $self->ReadString(1), 207 | Tasks => $self->ReadString(1), 208 | Languages => $self->ReadString(1), 209 | Check => $self->ReadString(1), 210 | MinVersion => { 211 | WinVersion => $self->ReadCardinal(), 212 | NTVersion => $self->ReadCardinal(), 213 | NTServicePack => $self->ReadWord(), 214 | }, 215 | OnlyBelowVersion => { 216 | WinVersion => $self->ReadCardinal(), 217 | NTVersion => $self->ReadCardinal(), 218 | NTServicePack => $self->ReadWord(), 219 | }, 220 | LocationEntry => $self->ReadInteger(), 221 | Attribs => $self->ReadInteger(), 222 | ExternalSize => $self->ReadInt64(), 223 | Options => $self->ReadSet([ 'foConfirmOverwrite', 'foUninsNeverUninstall', 'foRestartReplace', 'foDeleteAfterInstall', 'foRegisterServer', 'foRegisterTypeLib', 'foSharedFile', 'foCompareTimeStamp', 'foFontIsntTrueType', 'foSkipIfSourceDoesntExist', 'foOverwriteReadOnly', 'foOverwriteSameVersion', 'foCustomDestName', 'foOnlyIfDestFileExists', 'foNoRegError', 'foUninsRestartDelete', 'foOnlyIfDoesntExist', 'foIgnoreVersion', 'foPromptIfOlder', 'foDontCopy', 'foUninsRemoveReadOnly' ]), 224 | FileType => $self->ReadEnum([ 'ftUserFile', 'ftUninstExe', 'ftRegSvrExe' ]), 225 | }; 226 | return $ret; 227 | } 228 | sub TSetupIconEntry { 229 | my ($self) = @_; 230 | my $ret; 231 | $ret = { 232 | IconName => $self->ReadString(1), 233 | Filename => $self->ReadString(1), 234 | Parameters => $self->ReadString(1), 235 | WorkingDir => $self->ReadString(1), 236 | IconFilename => $self->ReadString(1), 237 | Comment => $self->ReadString(1), 238 | Components => $self->ReadString(1), 239 | Tasks => $self->ReadString(1), 240 | Languages => $self->ReadString(1), 241 | Check => $self->ReadString(1), 242 | MinVersion => { 243 | WinVersion => $self->ReadCardinal(), 244 | NTVersion => $self->ReadCardinal(), 245 | NTServicePack => $self->ReadWord(), 246 | }, 247 | OnlyBelowVersion => { 248 | WinVersion => $self->ReadCardinal(), 249 | NTVersion => $self->ReadCardinal(), 250 | NTServicePack => $self->ReadWord(), 251 | }, 252 | IconIndex => $self->ReadInteger(), 253 | ShowCmd => $self->ReadInteger(), 254 | CloseOnExit => $self->ReadEnum([ 'icNoSetting', 'icYes', 'icNo' ]), 255 | HotKey => $self->ReadWord(), 256 | Options => $self->ReadSet([ 'ioUninsNeverUninstall', 'ioCreateOnlyIfFileExists', 'ioUseAppPaths' ]), 257 | }; 258 | return $ret; 259 | } 260 | sub TSetupIniEntry { 261 | my ($self) = @_; 262 | my $ret; 263 | $ret = { 264 | Filename => $self->ReadString(1), 265 | Section => $self->ReadString(1), 266 | Entry => $self->ReadString(1), 267 | Value => $self->ReadString(1), 268 | Components => $self->ReadString(1), 269 | Tasks => $self->ReadString(1), 270 | Languages => $self->ReadString(1), 271 | Check => $self->ReadString(1), 272 | MinVersion => { 273 | WinVersion => $self->ReadCardinal(), 274 | NTVersion => $self->ReadCardinal(), 275 | NTServicePack => $self->ReadWord(), 276 | }, 277 | OnlyBelowVersion => { 278 | WinVersion => $self->ReadCardinal(), 279 | NTVersion => $self->ReadCardinal(), 280 | NTServicePack => $self->ReadWord(), 281 | }, 282 | Options => $self->ReadSet([ 'ioCreateKeyIfDoesntExist', 'ioUninsDeleteEntry', 'ioUninsDeleteEntireSection', 'ioUninsDeleteSectionIfEmpty', 'ioHasValue' ]), 283 | }; 284 | return $ret; 285 | } 286 | sub TSetupRegistryEntry { 287 | my ($self) = @_; 288 | my $ret; 289 | $ret = { 290 | Subkey => $self->ReadString(1), 291 | ValueName => $self->ReadString(1), 292 | ValueData => $self->ReadString(1), 293 | Components => $self->ReadString(1), 294 | Tasks => $self->ReadString(1), 295 | Languages => $self->ReadString(1), 296 | Check => $self->ReadString(1), 297 | Permissions => $self->ReadString(1), 298 | MinVersion => { 299 | WinVersion => $self->ReadCardinal(), 300 | NTVersion => $self->ReadCardinal(), 301 | NTServicePack => $self->ReadWord(), 302 | }, 303 | OnlyBelowVersion => { 304 | WinVersion => $self->ReadCardinal(), 305 | NTVersion => $self->ReadCardinal(), 306 | NTServicePack => $self->ReadWord(), 307 | }, 308 | RootKey => $self->HKEY(), 309 | Typ => $self->ReadEnum([ 'rtNone', 'rtString', 'rtExpandString', 'rtDWord', 'rtBinary', 'rtMultiString' ]), 310 | Options => $self->ReadSet([ 'roCreateValueIfDoesntExist', 'roUninsDeleteValue', 'roUninsClearValue', 'roUninsDeleteEntireKey', 'roUninsDeleteEntireKeyIfEmpty', 'roPreserveStringType', 'roDeleteKey', 'roDeleteValue', 'roNoError', 'roDontCreateKey' ]), 311 | }; 312 | return $ret; 313 | } 314 | sub TSetupDeleteEntry { 315 | my ($self) = @_; 316 | my $ret; 317 | $ret = { 318 | Name => $self->ReadString(1), 319 | Components => $self->ReadString(1), 320 | Tasks => $self->ReadString(1), 321 | Languages => $self->ReadString(1), 322 | Check => $self->ReadString(1), 323 | MinVersion => { 324 | WinVersion => $self->ReadCardinal(), 325 | NTVersion => $self->ReadCardinal(), 326 | NTServicePack => $self->ReadWord(), 327 | }, 328 | OnlyBelowVersion => { 329 | WinVersion => $self->ReadCardinal(), 330 | NTVersion => $self->ReadCardinal(), 331 | NTServicePack => $self->ReadWord(), 332 | }, 333 | DeleteType => $self->ReadEnum([ 'dfFiles', 'dfFilesAndOrSubdirs', 'dfDirIfEmpty' ]), 334 | }; 335 | return $ret; 336 | } 337 | sub TSetupRunEntry { 338 | my ($self) = @_; 339 | my $ret; 340 | $ret = { 341 | Name => $self->ReadString(1), 342 | Parameters => $self->ReadString(1), 343 | WorkingDir => $self->ReadString(1), 344 | RunOnceId => $self->ReadString(1), 345 | StatusMsg => $self->ReadString(1), 346 | Description => $self->ReadString(1), 347 | Components => $self->ReadString(1), 348 | Tasks => $self->ReadString(1), 349 | Languages => $self->ReadString(1), 350 | Check => $self->ReadString(1), 351 | MinVersion => { 352 | WinVersion => $self->ReadCardinal(), 353 | NTVersion => $self->ReadCardinal(), 354 | NTServicePack => $self->ReadWord(), 355 | }, 356 | OnlyBelowVersion => { 357 | WinVersion => $self->ReadCardinal(), 358 | NTVersion => $self->ReadCardinal(), 359 | NTServicePack => $self->ReadWord(), 360 | }, 361 | ShowCmd => $self->ReadInteger(), 362 | Wait => $self->ReadEnum([ 'rwWaitUntilTerminated', 'rwNoWait', 'rwWaitUntilIdle' ]), 363 | Options => $self->ReadSet([ 'roShellExec', 'roSkipIfDoesntExist', 'roPostInstall', 'roUnchecked', 'roSkipIfSilent', 'roSkipIfNotSilent', 'roHideWizard' ]), 364 | }; 365 | return $ret; 366 | } 367 | sub TSetupFileLocationEntry { 368 | my ($self) = @_; 369 | my $ret; 370 | $ret = { 371 | FirstSlice => $self->ReadInteger(), 372 | LastSlice => $self->ReadInteger(), 373 | StartOffset => $self->ReadLongInt(), 374 | ChunkSuboffset => $self->ReadInt64(), 375 | OriginalSize => $self->ReadInt64(), 376 | ChunkCompressedSize => $self->ReadInt64(), 377 | CRC => $self->ReadLongInt(), 378 | TimeStamp => $self->TFileTime(), 379 | FileVersionMS => $self->DWORD(), 380 | FileVersionLS => $self->DWORD(), 381 | Flags => $self->ReadSet([ 'foVersionInfoValid', 'foVersionInfoNotValid', 'foTimeStampInUTC' ]), 382 | }; 383 | return $ret; 384 | } 385 | 1; 386 | -------------------------------------------------------------------------------- /Win/Exe.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe; 4 | 5 | use strict; 6 | use warnings; 7 | use Switch 'Perl6'; 8 | use IO::File; 9 | use Win::Exe::Util; 10 | use Win::Exe::DosHeader; 11 | use Win::Exe::PeHeader; 12 | use Win::Exe::OptionalHeader; 13 | use Win::Exe::Section; 14 | use Win::Exe::Table; 15 | use Carp; 16 | 17 | use overload ( 18 | '""' => \&Describe, 19 | ); 20 | 21 | sub new { 22 | my $class = shift; 23 | my ($io) = @_; 24 | 25 | my $self = { }; 26 | 27 | if (ref($io)) { 28 | # interpret as IO object or glob 29 | $self->{Input} = $io; 30 | } else { 31 | # interpret as filename 32 | $self->{Filename} = $io; 33 | $self->{Input} = IO::File->new($io, '<') || croak("Can't open $io"); 34 | $self->{Input}->binmode(); 35 | } 36 | 37 | return bless($self, $class); 38 | } 39 | 40 | sub DosHeader { 41 | my $self = shift; 42 | if (!$self->{DosHeader}) { 43 | $self->{DosHeader} = Win::Exe::DosHeader->new($self); 44 | } 45 | return $self->{DosHeader} 46 | } 47 | 48 | sub PeHeader { 49 | my $self = shift; 50 | if (!$self->{PeHeader}) { 51 | $self->IsPeExe() || croak("Can't find PE header"); 52 | $self->{PeHeader} = Win::Exe::PeHeader->new($self); 53 | } 54 | return $self->{PeHeader}; 55 | } 56 | 57 | sub OptionalHeader { 58 | my $self = shift; 59 | if (!$self->{OptionalHeader}) { 60 | $self->{OptionalHeader} = Win::Exe::OptionalHeader->new($self); 61 | } 62 | return $self->{OptionalHeader}; 63 | } 64 | 65 | sub Sections { 66 | my $self = shift; 67 | if (!$self->{Sections}) { 68 | $self->{Sections} = Win::Exe::Section->all($self); 69 | } 70 | return $self->{Sections}; 71 | } 72 | 73 | sub Tables { 74 | my $self = shift; 75 | if (!$self->{Tables}) { 76 | $self->{Tables} = Win::Exe::Table->all($self); 77 | } 78 | return $self->{Tables}; 79 | } 80 | 81 | sub PeOffset { 82 | return shift()->DosHeader()->{Lfanew}; 83 | } 84 | 85 | sub PeSectionOffset { 86 | my $self = shift; 87 | return $self->PeOffset() + 24 + $self->PeHeader()->{SizeOfOptionalHeader}; 88 | } 89 | 90 | sub IsPeExe { 91 | return shift()->PeOffset() ? 1 : 0; 92 | } 93 | 94 | sub FindVirtualAddress { 95 | my ($self, $address) = @_; 96 | for my $section (keys(%{$self->Sections()})) { 97 | my $pointer = $section->VaToPointer($address); 98 | if ($pointer != -1) { 99 | return $pointer; 100 | } 101 | } 102 | if ($address != 0) { 103 | #carp("Unmappable address $address"); 104 | } 105 | return -1; 106 | } 107 | 108 | sub FindVirtualRange { 109 | my ($self, $address, $size) = @_; 110 | for my $section (values(%{$self->Sections()})) { 111 | my $pointer = $section->VaToPointer($address); 112 | if ($pointer != -1) { 113 | my $end = $section->VaToPointer($address + $size); 114 | ($end != -1) && return $pointer; 115 | } 116 | } 117 | if ($size != 0) { 118 | #carp("Unmappable range ($address, $size)"); 119 | } 120 | return -1; 121 | } 122 | 123 | sub Describe { 124 | my $self = shift; 125 | my $ret; 126 | if ($self->{Filename}) { 127 | $ret .= $self->{Filename} . ":\n"; 128 | } else { 129 | print("EXE file:\n"); 130 | } 131 | $ret .= $self->DosHeader()->Describe(" "); 132 | $ret .= $self->PeHeader()->Describe(" "); 133 | $ret .= $self->OptionalHeader()->Describe(" "); 134 | $ret .= " Sections:\n"; 135 | for my $section (values(%{$self->Sections()})) { 136 | $ret .= $section->Describe(" "); 137 | } 138 | $ret .= " Tables:\n"; 139 | for my $table (values(%{$self->Tables()})) { 140 | $ret .= $table->Describe(" "); 141 | } 142 | return $ret; 143 | } 144 | 145 | sub FindResource { 146 | return FindResourceEx(@_[0..2], 0); 147 | } 148 | 149 | sub FindResourceEx { 150 | my ($self, $type, $resid, $lang) = @_; 151 | my $resgroup = $self->Tables()->{ResourceTable}->GetResource($type, $resid); 152 | # TODO: handle language precedence rules correctly 153 | my $resource = $resgroup->{Directory}->{Resources}->{$lang}->{Data}; 154 | my $offset = $self->FindVirtualRange($resource->{OffsetToData}, $resource->{Size}); 155 | if ($offset != -1) { 156 | $self->{Input}->seek($offset, 0); 157 | $self->{Input}->read(my $buffer, $resource->{Size}); 158 | return $buffer; 159 | } 160 | return undef; 161 | } 162 | 163 | 1; 164 | 165 | -------------------------------------------------------------------------------- /Win/Exe/DosHeader.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::DosHeader; 4 | 5 | use strict; 6 | use Win::Exe::Util; 7 | 8 | our $ExeMagic = 0x5A4D; # 'MZ' 9 | 10 | sub new { 11 | my ($class, $exe) = @_; 12 | my $buffer; 13 | $exe->{Input}->seek(0, 0); 14 | $exe->{Input}->read($buffer, 64); 15 | my $self = unpackbinary($buffer, '(S14a8S2a20l)<', 'Magic', 'Cblp', 'Cp', 'Crlc', 'Cparhdr', 'Minalloc', 'Maxalloc', 'Ss', 'Sp', 'Csum', 'Ip', 'Cs', 'Lfarlc', 'Ovno', 'Res', 'Oemid', 'Oeminfo', 'Res2', 'Lfanew'); 16 | ($self->{Magic} == $ExeMagic) || die("Invalid magic, this is not an EXE file"); 17 | return bless($self, $class); 18 | } 19 | 20 | sub Describe { 21 | my $self = shift; 22 | my $prefix = (@_ > 0) ? $_[0] : ''; 23 | my $ret = "${prefix}DOS header:\n"; 24 | $prefix .= " "; 25 | $ret .= sprintf("${prefix}Magic: '%c%c'\n", $self->{Magic} & 0xff, $self->{Magic} >> 8); 26 | $ret .= sprintf("${prefix}EXE size: %u bytes (%u bytes in file)\n", $self->{Cp} * 512, $self->{Cp} * 512 - $self->{Cblp}); 27 | $ret .= sprintf("${prefix}Relocations: %u @ 0x%04x\n", $self->{Crlc}, $self->{Lfarlc}); 28 | $ret .= sprintf("${prefix}Header size: %u bytes\n", $self->{Cparhdr} * 16); 29 | $ret .= sprintf("${prefix}Minimum/maximum allocation: %u / %u bytes\n", $self->{Minalloc} * 16, $self->{Maxalloc} * 16); 30 | $ret .= sprintf("${prefix}SS:SP: %04x:%04x\n", $self->{Ss}, $self->{Sp}); 31 | $ret .= sprintf("${prefix}CS:IP: %04x:%04x\n", $self->{Cs}, $self->{Ip}); 32 | $ret .= sprintf("${prefix}Checksum: 0x%04x\n", $self->{Csum}); 33 | $ret .= sprintf("${prefix}Overlay: #%u\n", $self->{Ovno}); 34 | $ret .= sprintf("${prefix}OEM ID/info: 0x%04x / 0x%04x\n", $self->{Oemid}, $self->{Oeminfo}); 35 | $ret .= sprintf("${prefix}PE header: 0x%08x\n", $self->{Lfanew}); 36 | return $ret; 37 | } 38 | 39 | 1; 40 | 41 | -------------------------------------------------------------------------------- /Win/Exe/OptionalHeader.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::OptionalHeader; 4 | 5 | use strict; 6 | use Switch 'Perl6'; 7 | 8 | sub new { 9 | my ($class, $exe) = @_; 10 | given ($exe->PeHeader()->{Machine}) { 11 | when ($Win::Exe::PeHeader::Machine->{I386}) { 12 | return Win::Exe::OptionalHeader::I386->new($exe); 13 | } 14 | when ($Win::Exe::PeHeader::Machine->{Amd64}) { 15 | return Win::Exe::OptionalHeader::Amd64->new($exe); 16 | } 17 | default { 18 | die("Don't know how to interpret optional headers for architecture " . $exe->PeHeader()->MachineName()); 19 | } 20 | } 21 | warn("No interpretation for optional headers of machine type " . $exe->PeHeader()->MachineName() . " found, ignoring"); 22 | return bless({ }, $class); 23 | } 24 | 25 | package Win::Exe::OptionalHeader::I386; 26 | 27 | use strict; 28 | use Win::Exe::Util; 29 | 30 | our $Subsystem = { 31 | Native => 1, # image doesn't require a subsystem. 32 | WindowsGui => 2, # image runs in the windows gui subsystem. 33 | WindowsCui => 3, # image runs in the windows character subsystem. 34 | Os2Cui => 5, # image runs in the os/2 character subsystem. 35 | PosixCui => 7, # image runs in the posix character subsystem. 36 | NativeWindows => 8, # image is a native win9x driver. 37 | WindowsCeGui => 9, # image runs in the windows ce subsystem. 38 | EfiApplication => 10, 39 | EfiBootServiceDriver => 11, 40 | EfiRuntimeDriver => 12, 41 | EfiRom => 13, 42 | Xbox => 14, 43 | WindowsBootApplication => 16, 44 | }; 45 | our $Win32Characteristic = { 46 | DynamicBase => 0x0040, # DLL can move (ASLR) 47 | ForceIntegrity => 0x0080, # Code Integrity Image 48 | NxCompat => 0x0100, # Image is NX compatible 49 | NoIsolation => 0x0200, # Image understands isolation and doesn't want it 50 | NoSeh => 0x0400, # Image does not use SEH. No SE handler may reside in this image 51 | NoBind => 0x0800, # Do not bind this image. 52 | WdmDriver => 0x2000, # Driver uses WDM model 53 | TerminalServerAware => 0x8000, 54 | }; 55 | our $Win32Magic = 0x010B; 56 | 57 | sub new { 58 | my $class = shift; 59 | my ($exe) = @_; 60 | 61 | my $buffer; 62 | $exe->{Input}->seek($exe->PeOffset() + 24, 0); 63 | $exe->{Input}->read($buffer, 224 - 8*16); 64 | my $self = unpackbinary($buffer, '(SC2L9S6L4S2L6)<', 'Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode', 'SizeOfInitializedData', 65 | 'SizeOfUninitializedData', 'AddressOfEntryPoint', 'BaseOfCode', 'BaseOfData', 'ImageBase', 'SectionAlignment', 'FileAlignment', 'MajorOperatingSystemVersion', 66 | 'MinorOperatingSystemVersion', 'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion', 'MinorSubsystemVersion', 'Win32VersionValue', 'SizeOfImage', 'SizeOfHeaders', 67 | 'CheckSum', 'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit', 'SizeOfHeapReserve', 'SizeOfHeapCommit', 68 | 'LoaderFlags', 'NumberOfRvaAndSizes'); 69 | ($self->{Magic} == $Win32Magic) || die("Invalid optional header magic"); 70 | my @Keys = ('ExportTable', 'ImportTable', 'ResourceTable', 'ExceptionTable', 'SecurityTable', 'BaserelocTable', 'DebugDirectory', 71 | 'Architecture', 'GlobalPtr', 'TlsTable', 'LoadConfigTable', 'BoundImportTable', 'IatTable', 'DelayImportTable', 'ComDescriptor'); 72 | $self->{DataDirectory} = { }; 73 | for (my $i = 0; $i < 16; $i++) { 74 | $exe->{Input}->read($buffer, 8); 75 | if ($i < @Keys) { 76 | $self->{DataDirectory}->{$Keys[$i]} = unpackbinary($buffer, '(L2)<', 'VirtualAddress', 'Size'); 77 | } 78 | } 79 | 80 | return bless($self, $class); 81 | } 82 | 83 | sub Describe { 84 | my $self = shift; 85 | my $prefix = (@_ > 0) ? $_[0] : ''; 86 | my $ret = "${prefix}Win32 header:\n"; 87 | $prefix .= " "; 88 | $ret .= sprintf("${prefix}Magic: 0x%04x\n", $self->{Magic}); 89 | $ret .= sprintf("${prefix}Linker version: %u.%u\n", $self->{MajorLinkerVersion}, $self->{MinorLinkerVersion}); 90 | $ret .= sprintf("${prefix}OS version: %u.%u\n", $self->{MajorOperatingSystemVersion}, $self->{MinorOperatingSystemVersion}); 91 | $ret .= sprintf("${prefix}Image version: %u.%u\n", $self->{MajorImageVersion}, $self->{MinorImageVersion}); 92 | $ret .= sprintf("${prefix}Subsystem: %s %u.%u\n", $self->SubsystemName(), $self->{MajorSubsystemVersion}, $self->{MinorSubsystemVersion}); 93 | $ret .= sprintf("${prefix}Win32 version: %u\n", $self->{Win32VersionValue}); 94 | $ret .= sprintf("${prefix}Entry point: 0x%08x\n", $self->{AddressOfEntryPoint}); 95 | $ret .= sprintf("${prefix}Code base address: 0x%08x\n", $self->{BaseOfCode}); 96 | $ret .= sprintf("${prefix}Data base address: 0x%08x\n", $self->{BaseOfData}); 97 | $ret .= sprintf("${prefix}Image base address: 0x%08x\n", $self->{ImageBase}); 98 | $ret .= sprintf("${prefix}Code size: %u bytes\n", $self->{SizeOfCode}); 99 | $ret .= sprintf("${prefix}Initialized data size: %u bytes\n", $self->{SizeOfInitializedData}); 100 | $ret .= sprintf("${prefix}Uninitialized data size: %u bytes\n", $self->{SizeOfUninitializedData}); 101 | $ret .= sprintf("${prefix}Image size: %u bytes\n", $self->{SizeOfImage}); 102 | $ret .= sprintf("${prefix}Header size: %u bytes\n", $self->{SizeOfHeaders}); 103 | $ret .= sprintf("${prefix}Stack reserve size: %u bytes\n", $self->{SizeOfStackReserve}); 104 | $ret .= sprintf("${prefix}Stack commit size: %u bytes\n", $self->{SizeOfStackCommit}); 105 | $ret .= sprintf("${prefix}Heap reserve size: %u bytes\n", $self->{SizeOfHeapReserve}); 106 | $ret .= sprintf("${prefix}Heap commit size: %u bytes\n", $self->{SizeOfHeapCommit}); 107 | $ret .= sprintf("${prefix}Section alignment: %u bytes\n", $self->{SectionAlignment}); 108 | $ret .= sprintf("${prefix}File alignment: %u bytes\n", $self->{FileAlignment}); 109 | $ret .= sprintf("${prefix}Checksum: 0x%08x\n", $self->{CheckSum}); 110 | $ret .= sprintf("${prefix}DLL characteristics: %s\n", join(' ', $self->Characteristics())); 111 | $ret .= sprintf("${prefix}Loader flags: 0x%08x\n", $self->{LoaderFlags}); 112 | $ret .= sprintf("${prefix}RVA and size entries: %u\n", $self->{NumberOfRvaAndSizes}); 113 | $ret .= sprintf("${prefix}Tables:\n"); 114 | for my $key (keys(%{$self->{DataDirectory}})) { 115 | my $entry = $self->{DataDirectory}->{$key}; 116 | if ($entry->{VirtualAddress} && $entry->{Size}) { 117 | $ret .= sprintf("${prefix} %s: %u bytes @ 0x%08x\n", $key, $entry->{Size}, $entry->{VirtualAddress}); 118 | } 119 | } 120 | return $ret; 121 | } 122 | 123 | sub Characteristics { 124 | my $self = shift; 125 | my @characteristics; 126 | for my $key (keys(%{$Win32Characteristic})) { 127 | if ($self->{DllCharacteristics} & $Win32Characteristic->{$key}) { 128 | push(@characteristics, $key); 129 | } 130 | } 131 | return @characteristics; 132 | } 133 | 134 | sub SubsystemName { 135 | my $self = shift; 136 | my @names = map { $self->{Subsystem} == $Subsystem->{$_} ? $_ : () } keys(%{$Subsystem}); 137 | return $names[0]; 138 | } 139 | 140 | sub HasTables { 141 | return defined(shift()->{DataDirectory}); 142 | } 143 | 144 | 1; 145 | 146 | -------------------------------------------------------------------------------- /Win/Exe/PeHeader.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::PeHeader; 4 | 5 | use strict; 6 | use Switch 'Perl6'; 7 | use Win::Exe::Util; 8 | 9 | our $PeMagic = 0x00004550; # 'PE\0\0' 10 | our $Machine = { 11 | I860 => 0x14d, # Intel i860 12 | I386 => 0x14c, # Intel I386 (same ID used for 486 and 586) 13 | Mips3000 => 0x162, # MIPS R3000 14 | Mips4000 => 0x166, # MIPS R4000 15 | Alpha => 0x183, # DEC Alpha AXP 16 | Amd64 => 0x8664, 17 | }; 18 | our $PeCharacteristic = { 19 | RelocsStripped => 0x0001, # relocation info stripped from file. 20 | ExecutableImage => 0x0002, # file is executable (i.e. no unresolved externel references). 21 | LineNumsStripped => 0x0004, # line nunbers stripped from file. 22 | LocalSymsStripped => 0x0008, # local symbols stripped from file. 23 | AggresiveWsTrim => 0x0010, # agressively trim working set 24 | LargeAddressAware => 0x0020, # app can handle >2gb addresses 25 | BytesReversedLo => 0x0080, # bytes of machine word are reversed. 26 | Machine32bit => 0x0100, # 32 bit word machine. 27 | DebugStripped => 0x0200, # debugging info stripped from file in .dbg file 28 | RemovableRunFromSwap => 0x0400, # if image is on removable media, copy and run from the swap file. 29 | NetRunFromSwap => 0x0800, # if image is on net, copy and run from the swap file. 30 | System => 0x1000, # system file. 31 | Dll => 0x2000, # file is a dll. 32 | UpSystem_only => 0x4000, # file should only be run on a up machine 33 | BytesReversedHi => 0x8000, # bytes of machine word are reversed. 34 | }; 35 | our $Subsystem = { 36 | Native => 1, # image doesn't require a subsystem. 37 | WindowsGui => 2, # image runs in the windows gui subsystem. 38 | WindowsCui => 3, # image runs in the windows character subsystem. 39 | Os2Cui => 5, # image runs in the os/2 character subsystem. 40 | PosixCui => 7, # image runs in the posix character subsystem. 41 | NativeWindows => 8, # image is a native win9x driver. 42 | WindowsCeGui => 9, # image runs in the windows ce subsystem. 43 | EfiApplication => 10, 44 | EfiBootServiceDriver => 11, 45 | EfiRuntimeDriver => 12, 46 | EfiRom => 13, 47 | Xbox => 14, 48 | WindowsBootApplication => 16, 49 | }; 50 | our $Win32Characteristic = { 51 | DynamicBase => 0x0040, # DLL can move (ASLR) 52 | ForceIntegrity => 0x0080, # Code Integrity Image 53 | NxCompat => 0x0100, # Image is NX compatible 54 | NoIsolation => 0x0200, # Image understands isolation and doesn't want it 55 | NoSeh => 0x0400, # Image does not use SEH. No SE handler may reside in this image 56 | NoBind => 0x0800, # Do not bind this image. 57 | WdmDriver => 0x2000, # Driver uses WDM model 58 | TerminalServerAware => 0x8000, 59 | }; 60 | our $Win32Magic = 0x010B; 61 | our $Win64Magic = 0x020B; 62 | our $SectionCharacteristic = { 63 | TypeReg => 0x00000000, 64 | TypeDsect => 0x00000001, 65 | TypeNoload => 0x00000002, 66 | TypeGroup => 0x00000004, 67 | TypeNoPad => 0x00000008, 68 | TypeCopy => 0x00000010, 69 | CntCode => 0x00000020, # section contains code. 70 | CntInitializedData => 0x00000040, # section contains initialized data. 71 | CntUnitializedData => 0x00000080, # section contains uninitialized data. 72 | LnkOther => 0x00000100, 73 | LnkInfo => 0x00000200, # section contains comments or some other type of information. 74 | TypeOver => 0x00000400, 75 | LnkRemove => 0x00000800, # section contents will not become part of image. 76 | LnkComdat => 0x00001000, # section contents comdat. 77 | MemProtected => 0x00004000, # obsolete 78 | NoDeferSpecExc => 0x00004000, # reset speculative exceptions handling bits in the tlb entries for this section. 79 | Gprel => 0x00008000, # section content can be accessed relative to gp 80 | MemFardata => 0x00008000, 81 | MemSysheap => 0x00010000, # obsolete 82 | MemPurgeable => 0x00020000, 83 | Mem16bit => 0x00020000, 84 | MemLocked => 0x00040000, 85 | MemPreload => 0x00080000, 86 | Align1bytes => 0x00100000, 87 | Align2bytes => 0x00200000, 88 | Align4bytes => 0x00300000, 89 | Align8bytes => 0x00400000, 90 | Align16bytes => 0x00500000, # default alignment if no others are specified. 91 | Align32bytes => 0x00600000, 92 | Align64bytes => 0x00700000, 93 | Align128bytes => 0x00800000, 94 | Align256bytes => 0x00900000, 95 | Align512bytes => 0x00a00000, 96 | Align1024bytes => 0x00b00000, 97 | Align2048bytes => 0x00c00000, 98 | Align4096bytes => 0x00d00000, 99 | Align8192bytes => 0x00e00000, 100 | AlignMask => 0x00f00000, 101 | LnkNrelocOvfl => 0x01000000, # section contains extended relocations. 102 | MemDiscardable => 0x02000000, # section can be discarded. 103 | MemNotCached => 0x04000000, # section is not cachable. 104 | MemNotPaged => 0x08000000, # section is not pageable. 105 | MemShared => 0x10000000, # section is shareable. 106 | MemExecute => 0x20000000, # section is executable. 107 | MemRead => 0x40000000, # section is readable. 108 | MemWrite => 0x80000000, # section is writeable. 109 | }; 110 | 111 | sub new { 112 | my $class = shift; 113 | my ($exe) = @_; 114 | 115 | my $buffer; 116 | $exe->{Input}->seek($exe->PeOffset(), 0); 117 | $exe->{Input}->read($buffer, 24); 118 | my $self = unpackbinary($buffer, '(LS2L3S2)<', 'Signature', 'Machine', 'NumberOfSections', 'TimeDateStamp', 'PointerToSymbolTable', 'NumberOfSymbols', 'SizeOfOptionalHeader', 'Characteristics'); 119 | ($self->{Signature} == $PeMagic) || die("Invalid PE magic"); 120 | 121 | return bless($self, $class); 122 | } 123 | 124 | sub Describe { 125 | my $self = shift; 126 | my $prefix = (@_ > 0) ? $_[0] : ''; 127 | my $ret = "${prefix}PE header:\n"; 128 | $prefix .= " "; 129 | $ret .= sprintf("${prefix}Signature: 0x%08x\n", $self->{Signature}); 130 | $ret .= sprintf("${prefix}Machine: %s\n", $self->MachineName()); 131 | $ret .= sprintf("${prefix}Number of sections: %u\n", $self->{NumberOfSections}); 132 | $ret .= sprintf("${prefix}Date/time: %s\n", coffdate($self->{TimeDateStamp})->format_cldr('yyyy-MM-dd HH:mm:ss')); 133 | $ret .= sprintf("${prefix}Symbols: %u @ 0x%08x\n", $self->{NumberOfSymbols}, $self->{PointerToSymbolTable}); 134 | $ret .= sprintf("${prefix}Size of optional header: %u bytes\n", $self->{SizeOfOptionalHeader}); 135 | $ret .= sprintf("${prefix}Characteristics: %s\n", join(' ', $self->Characteristics())); 136 | return $ret; 137 | } 138 | 139 | sub MachineName { 140 | my $self = shift; 141 | given ($self->{Machine}) { 142 | when ($Machine->{I860}) { 143 | return 'Intel i860'; 144 | } 145 | when ($Machine->{I386}) { 146 | return 'Intel i386'; 147 | } 148 | when ($Machine->{Mips3000}) { 149 | return 'MIPS R3000'; 150 | } 151 | when ($Machine->{Mips4000}) { 152 | return 'MIPS R4000'; 153 | } 154 | when ($Machine->{Alpha}) { 155 | return 'DEC Alpha AXP'; 156 | } 157 | when ($Machine->{Amd64}) { 158 | return 'AMD64'; 159 | } 160 | } 161 | return sprintf("Unknown (0x%04x)", $self->{Machine}); 162 | } 163 | 164 | sub Characteristics { 165 | my $self = shift; 166 | my @characteristics; 167 | for my $key (keys(%{$PeCharacteristic})) { 168 | if ($self->{Characteristics} & $PeCharacteristic->{$key}) { 169 | push(@characteristics, $key); 170 | } 171 | } 172 | return @characteristics; 173 | } 174 | 175 | 1; 176 | -------------------------------------------------------------------------------- /Win/Exe/Section.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::Section; 4 | 5 | use strict; 6 | use Win::Exe::Util; 7 | 8 | our $SectionCharacteristic = { 9 | TypeReg => 0x00000000, 10 | TypeDsect => 0x00000001, 11 | TypeNoload => 0x00000002, 12 | TypeGroup => 0x00000004, 13 | TypeNoPad => 0x00000008, 14 | TypeCopy => 0x00000010, 15 | CntCode => 0x00000020, # section contains code. 16 | CntInitializedData => 0x00000040, # section contains initialized data. 17 | CntUnitializedData => 0x00000080, # section contains uninitialized data. 18 | LnkOther => 0x00000100, 19 | LnkInfo => 0x00000200, # section contains comments or some other type of information. 20 | TypeOver => 0x00000400, 21 | LnkRemove => 0x00000800, # section contents will not become part of image. 22 | LnkComdat => 0x00001000, # section contents comdat. 23 | MemProtected => 0x00004000, # obsolete 24 | NoDeferSpecExc => 0x00004000, # reset speculative exceptions handling bits in the tlb entries for this section. 25 | Gprel => 0x00008000, # section content can be accessed relative to gp 26 | MemFardata => 0x00008000, 27 | MemSysheap => 0x00010000, # obsolete 28 | MemPurgeable => 0x00020000, 29 | Mem16bit => 0x00020000, 30 | MemLocked => 0x00040000, 31 | MemPreload => 0x00080000, 32 | Align1bytes => 0x00100000, 33 | Align2bytes => 0x00200000, 34 | Align4bytes => 0x00300000, 35 | Align8bytes => 0x00400000, 36 | Align16bytes => 0x00500000, # default alignment if no others are specified. 37 | Align32bytes => 0x00600000, 38 | Align64bytes => 0x00700000, 39 | Align128bytes => 0x00800000, 40 | Align256bytes => 0x00900000, 41 | Align512bytes => 0x00a00000, 42 | Align1024bytes => 0x00b00000, 43 | Align2048bytes => 0x00c00000, 44 | Align4096bytes => 0x00d00000, 45 | Align8192bytes => 0x00e00000, 46 | AlignMask => 0x00f00000, 47 | LnkNrelocOvfl => 0x01000000, # section contains extended relocations. 48 | MemDiscardable => 0x02000000, # section can be discarded. 49 | MemNotCached => 0x04000000, # section is not cachable. 50 | MemNotPaged => 0x08000000, # section is not pageable. 51 | MemShared => 0x10000000, # section is shareable. 52 | MemExecute => 0x20000000, # section is executable. 53 | MemRead => 0x40000000, # section is readable. 54 | MemWrite => 0x80000000, # section is writeable. 55 | }; 56 | 57 | sub all { 58 | my ($class, $exe) = @_; 59 | my $ret = { }; 60 | for (my $i = 0; $i < $exe->PeHeader()->{NumberOfSections}; $i++) { 61 | my $section = $class->new($exe, $i); 62 | $ret->{$section->{Name}} = $section; 63 | } 64 | return $ret; 65 | } 66 | 67 | sub new { 68 | my $class = shift; 69 | my ($exe, $number) = @_; 70 | 71 | my $buffer; 72 | $exe->{Input}->seek($exe->PeSectionOffset() + $number * 40, 0); 73 | $exe->{Input}->read($buffer, 40); 74 | my $self = unpackbinary($buffer, '(Z8L6S2L)<', 'Name', 'VirtualSize', 'VirtualAddress', 'SizeOfRawData', 'PointerToRawData', 'PointerToRelocations', 'PointerToLinenumbers', 'NumberOfRelocations', 'NumberOfLineNumbers', 'Characteristics'); 75 | 76 | return bless($self, $class); 77 | } 78 | 79 | sub Describe { 80 | my $self = shift; 81 | my $prefix = (@_ > 0) ? $_[0] : ''; 82 | my $ret = sprintf("${prefix}Section %s:\n", $self->{Name}); 83 | $prefix .= " "; 84 | $ret .= sprintf("${prefix}Virtual memory: %u bytes @ 0x%08x\n", $self->{VirtualSize}, $self->{VirtualAddress}); 85 | $ret .= sprintf("${prefix}Raw data: %u bytes @ 0x%08x\n", $self->{SizeOfRawData}, $self->{PointerToRawData}); 86 | $ret .= sprintf("${prefix}Relocations: %u @ 0x%08x\n", $self->{NumberOfRelocations}, $self->{PointerToRelocations}); 87 | $ret .= sprintf("${prefix}Line numbers: %u @ 0x%08x\n", $self->{NumberOfLineNumbers}, $self->{PointerToLinenumbers}); 88 | $ret .= sprintf("${prefix}Characteristics: %s\n", join(' ', $self->Characteristics())); 89 | return $ret; 90 | } 91 | 92 | sub Characteristics { 93 | my $self = shift; 94 | my @characteristics; 95 | for my $key (keys(%{$SectionCharacteristic})) { 96 | if ($self->{Characteristics} & $SectionCharacteristic->{$key}) { 97 | push(@characteristics, $key); 98 | } 99 | } 100 | return @characteristics; 101 | } 102 | 103 | sub VaToPointer { 104 | my ($self, $va) = @_; 105 | # VA must be mappable to the file, VM page padding is not allowed 106 | if ($self->{VirtualAddress} <= $va && $va <= $self->{VirtualAddress} + $self->{SizeOfRawData}) { 107 | return $va - $self->{VirtualAddress} + $self->{PointerToRawData}; 108 | } 109 | return -1; 110 | } 111 | 112 | sub PointerToVa { 113 | my ($self, $pointer) = @_; 114 | if ($self->{PointerToRawData} <= $pointer && $pointer <= $self->{PointerToRawData} + $self->{SizeOfRawData}) { 115 | return $pointer - $self->{PointerToRawData} + $self->{VirtualAddress}; 116 | } 117 | return -1; 118 | } 119 | 120 | 1; 121 | 122 | -------------------------------------------------------------------------------- /Win/Exe/Table.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::Table; 4 | 5 | use strict; 6 | use Switch 'Perl6'; 7 | use Carp; 8 | 9 | sub all { 10 | my ($class, $exe) = @_; 11 | $exe->OptionalHeader()->HasTables() || croak("Executable type doesn't support tables"); 12 | my $ret = { }; 13 | for my $name (keys(%{$exe->OptionalHeader()->{DataDirectory}})) { 14 | $ret->{$name} = $class->new($exe, $name); 15 | } 16 | return $ret; 17 | } 18 | 19 | sub new { 20 | my ($class, $exe, $name) = @_; 21 | my $va = $exe->OptionalHeader()->{DataDirectory}->{$name}; 22 | my $pointer = $exe->FindVirtualRange($va->{VirtualAddress}, $va->{Size}); 23 | if ($pointer != -1) { 24 | given ($name) { 25 | when ('ExportTable') { 26 | } 27 | when ('ImportTable') { 28 | } 29 | when ('ResourceTable') { 30 | return Win::Exe::Table::Resource->new($exe, $pointer); 31 | } 32 | when ('ExceptionTable') { 33 | } 34 | when ('SecurityTable') { 35 | } 36 | when ('BaserelocTable') { 37 | } 38 | when ('DebugDirectory') { 39 | } 40 | when ('Architecture') { 41 | } 42 | when ('GlobalPtr') { 43 | } 44 | when ('TlsTable') { 45 | } 46 | when ('LoadConfigTable') { 47 | } 48 | when ('BoundImportTable') { 49 | } 50 | when ('IatTable') { 51 | } 52 | when ('DelayImportTable') { 53 | } 54 | when ('ComDescriptor') { 55 | } 56 | } 57 | } 58 | #carp("No interpretation for tables of type $name found, ignoring"); 59 | return bless({ }, $class); 60 | } 61 | 62 | sub Describe { 63 | return ""; 64 | } 65 | 66 | package Win::Exe::Table::Resource; 67 | 68 | use strict; 69 | use Encode qw(decode); 70 | use Win::Exe::Util; 71 | #use File::Type; 72 | 73 | #our $FileType = File::Type->new(); 74 | our $ResourceTypes = { 75 | Accelerator => 9, # Accelerator table. 76 | AniCursor => 21, # Animated cursor. 77 | AniIcon => 22, # Animated icon. 78 | Bitmap => 2, # Bitmap resource. 79 | Cursor => 1, # Hardware-dependent cursor resource. 80 | Dialog => 5, # Dialog box. 81 | DlgInclude => 17, # Allows a resource editing tool to associate a string with an .rc file. Typically, the string is the name of the header file that provides symbolic names. The resource compiler parses the string but otherwise ignores the value. For example, 1 DLGINCLUDE "MyFile.h" 82 | Font => 8, # Font resource. 83 | FontDir => 7, # Font directory resource. 84 | GroupCursor => 12, # Hardware-independent cursor resource. 85 | GroupIcon => 14, # Hardware-independent icon resource. 86 | Html => 23, # HTML resource. 87 | Icon => 3, # Hardware-dependent icon resource. 88 | Manifest => 24, # Side-by-Side Assembly Manifest. 89 | Menu => 4, # Menu resource. 90 | MessageTable => 11, # Message-table entry. 91 | PlugPlay => 19, # Plug and Play resource. 92 | RcData => 10, # Application-defined resource (raw data). 93 | String => 6, # String-table entry. 94 | Version => 16, # Version resource. 95 | Vxd => 20, # VXD. 96 | }; 97 | 98 | sub parsedirectory { 99 | my ($exe, $pointer, $offset) = @_; 100 | #printf("0x%08x\n", $pointer + $offset); 101 | my $buffer; 102 | $exe->{Input}->seek($pointer + $offset, 0); 103 | $exe->{Input}->read($buffer, 16); 104 | my $dir = unpackbinary($buffer, '(L2S4)<', 'Characteristics', 'TimeDateStamp', 'MajorVersion', 'MinorVersion', 'NumberOfNamedEntries', 'NumberOfIdEntries'); 105 | $dir->{Resources} = { }; 106 | for (my $i = 0; $i < $dir->{NumberOfNamedEntries} + $dir->{NumberOfIdEntries}; $i++) { 107 | $exe->{Input}->seek($pointer + $offset + 16 + $i * 8, 0); 108 | $exe->{Input}->read($buffer, 8); 109 | my $entry = unpackbinary($buffer, '(L2)<', 'Name', 'OffsetToDirectory'); 110 | $entry->{NameIsString} = ($entry->{Name} & 0x80000000) >> 31; 111 | $entry->{DataIsDirectory} = ($entry->{OffsetToDirectory} & 0x80000000) >> 31; 112 | $entry->{OffsetToDirectory} = $entry->{OffsetToDirectory} & 0x7fffffff; 113 | if ($entry->{DataIsDirectory}) { 114 | $entry->{Directory} = parsedirectory($exe, $pointer, $entry->{OffsetToDirectory}); 115 | } else { 116 | $exe->{Input}->seek($pointer + $entry->{OffsetToDirectory}, 0); 117 | $exe->{Input}->read($buffer, 32); 118 | $entry->{Data} = unpackbinary($buffer, '(L4)<', 'OffsetToData', 'Size', 'CodePage', 'Reserved'); 119 | # Data.OffsetToData is an RVA 120 | #$exe->{Input}->seek($pointer + $entry->{Data}->{OffsetToData}, 0); 121 | #$exe->{Input}->read($entry->{Data}->{Data}, $entry->{Data}->{Size}); 122 | #$entry->{Data}->{MimeType} = FileType->checktype_contents($entry->{Data}->{Data}); 123 | } 124 | if ($entry->{NameIsString}) { 125 | $entry->{NameOffset} = $entry->{Name} & 0x7fffffff; 126 | $exe->{Input}->seek($pointer + $entry->{OffsetToDirectory}, 0); 127 | $exe->{Input}->read($buffer, 2); 128 | $entry->{NameLength} = unpack('S<', $buffer); 129 | $exe->{Input}->read($buffer, $entry->{NameLength}); 130 | $entry->{Id} = decode('UTF-16LE', $buffer); 131 | } else { 132 | # is this 31bit or 16bit? 133 | $entry->{Id} = $entry->{Name} & 0xffff; 134 | } 135 | $dir->{Resources}->{$entry->{Id}} = $entry; 136 | } 137 | return $dir; 138 | } 139 | 140 | sub new { 141 | my ($class, $exe, $pointer) = @_; 142 | my $self = parsedirectory($exe, $pointer, 0); 143 | return bless($self, $class); 144 | } 145 | 146 | sub Describe { 147 | my $self = shift; 148 | my $prefix = (@_ > 0) ? $_[0] : ''; 149 | my $ret = sprintf("${prefix}Resource table:\n"); 150 | $prefix .= " "; 151 | $ret .= sprintf("${prefix}Date/time: %s\n", coffdate($self->{TimeDateStamp})->format_cldr('yyyy-MM-dd HH:mm:ss')); 152 | $ret .= sprintf("${prefix}Version: %u.%u\n", $self->{MajorVersion}, $self->{MinorVersion}); 153 | $ret .= sprintf("${prefix}Named entries: %u\n", $self->{NumberOfNamedEntries}); 154 | $ret .= sprintf("${prefix}ID entries: %u\n", $self->{NumberOfIdEntries}); 155 | return $ret; 156 | } 157 | 158 | sub GetResource { 159 | my ($self, $type, $resid) = @_; 160 | my $dir; 161 | if (defined($self->{Resources}->{$type})) { 162 | $dir = $self->{Resources}->{$type}; 163 | } elsif (defined($ResourceTypes->{$type})) { 164 | $dir = $self->{Resources}->{$ResourceTypes->{$type}}; 165 | } 166 | if ($dir) { 167 | if ($dir->{DataIsDirectory}) { 168 | return $dir->{Directory}->{Resources}->{$resid}; 169 | } 170 | } 171 | return undef; 172 | } 173 | 174 | 1; 175 | 176 | -------------------------------------------------------------------------------- /Win/Exe/Util.pm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | package Win::Exe::Util; 4 | 5 | use strict; 6 | use DateTime; 7 | use Exporter 'import'; 8 | our @EXPORT = qw(unpackbinary coffdate); 9 | 10 | # Unpack a binary data structure into a hashref 11 | sub unpackbinary { 12 | my ($buffer, $format, @keys) = @_; 13 | my @data = unpack($format, $buffer); 14 | my %ret; 15 | for (my $i = 0; $i < @data; $i++) { 16 | $ret{$keys[$i]} = $data[$i]; 17 | } 18 | return \%ret; 19 | } 20 | 21 | # Convert a COFF timestamp to a DateTime object 22 | sub coffdate { 23 | my ($stamp) = @_; 24 | return DateTime->new(year => 1970, month => 1, day => 1, hour => 0, minute => 0, second => 0)->add(seconds => $stamp); 25 | } 26 | 27 | 1; 28 | 29 | -------------------------------------------------------------------------------- /makeissrc.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | my $git = 'git'; 7 | my $unzip = 'unzip'; 8 | my $wget = 'wget'; 9 | my $srcdir = 'innosetup'; 10 | my $giturl = 'https://github.com/jrsoftware/issrc.git'; 11 | my $firstcommit = '238b7749629c219c25716a5f04a66fb9dfa5feb2'; 12 | my $branch = 'old'; 13 | my @sources = qw( 14 | http://www.jrsoftware.org/download.php/issrc-1.2.16.zip 15 | http://www.jrsoftware.org/download.php/issrc-1.3.26.zip 16 | http://www.jrsoftware.org/download.php/issrc-2.0.19.zip 17 | http://www.jrsoftware.org/download.php/issrc-3.0.7.zip 18 | http://files.jrsoftware.org/is/4/issrc-4.0.8.zip 19 | http://files.jrsoftware.org/is/4/issrc-4.0.9.zip 20 | http://files.jrsoftware.org/is/4/issrc-4.0.10.zip 21 | http://files.jrsoftware.org/is/4/issrc-4.0.11.zip 22 | http://files.jrsoftware.org/is/4/issrc-4.1.0.zip 23 | http://files.jrsoftware.org/is/4/issrc-4.1.1.zip 24 | http://files.jrsoftware.org/is/4/issrc-4.1.2.zip 25 | http://files.jrsoftware.org/is/4/issrc-4.1.3.zip 26 | http://files.jrsoftware.org/is/4/issrc-4.1.4.zip 27 | http://files.jrsoftware.org/is/4/issrc-4.1.5.zip 28 | http://files.jrsoftware.org/is/4/issrc-4.1.6.zip 29 | http://files.jrsoftware.org/is/4/issrc-4.1.7.zip 30 | http://files.jrsoftware.org/is/4/issrc-4.1.8.zip 31 | http://files.jrsoftware.org/is/4/issrc-4.2.0.zip 32 | http://files.jrsoftware.org/is/4/issrc-4.2.1.zip 33 | http://files.jrsoftware.org/is/4/issrc-4.2.2.zip 34 | http://files.jrsoftware.org/is/4/issrc-4.2.3.zip 35 | http://files.jrsoftware.org/is/4/issrc-4.2.4.zip 36 | http://files.jrsoftware.org/is/4/issrc-4.2.5.zip 37 | http://files.jrsoftware.org/is/4/issrc-4.2.6.zip 38 | http://files.jrsoftware.org/is/4/issrc-4.2.7.zip 39 | http://files.jrsoftware.org/is/5/issrc-5.0.0-beta.zip 40 | http://files.jrsoftware.org/is/5/issrc-5.0.1-beta.zip 41 | http://files.jrsoftware.org/is/5/issrc-5.0.2-beta.zip 42 | http://files.jrsoftware.org/is/5/issrc-5.0.3-beta.zip 43 | http://files.jrsoftware.org/is/5/issrc-5.0.4-beta.zip 44 | http://files.jrsoftware.org/is/5/issrc-5.0.5-beta.zip 45 | http://files.jrsoftware.org/is/5/issrc-5.0.6.zip 46 | http://files.jrsoftware.org/is/5/issrc-5.0.7.zip 47 | http://files.jrsoftware.org/is/5/issrc-5.0.8.zip 48 | http://files.jrsoftware.org/is/5/issrc-5.1.0-beta.zip 49 | http://files.jrsoftware.org/is/5/issrc-5.1.1-beta.zip 50 | http://files.jrsoftware.org/is/5/issrc-5.1.2-beta.zip 51 | http://files.jrsoftware.org/is/5/issrc-5.1.3-beta.zip 52 | http://files.jrsoftware.org/is/5/issrc-5.1.4.zip 53 | http://files.jrsoftware.org/is/5/issrc-5.1.5.zip 54 | http://files.jrsoftware.org/is/5/issrc-5.1.6.zip 55 | http://files.jrsoftware.org/is/5/issrc-5.1.7.zip 56 | http://files.jrsoftware.org/is/5/issrc-5.1.8.zip 57 | http://files.jrsoftware.org/is/5/issrc-5.1.9.zip 58 | http://files.jrsoftware.org/is/5/issrc-5.1.10.zip 59 | http://files.jrsoftware.org/is/5/issrc-5.1.11.zip 60 | http://files.jrsoftware.org/is/5/issrc-5.1.12.zip 61 | http://files.jrsoftware.org/is/5/issrc-5.1.13.zip 62 | http://files.jrsoftware.org/is/5/issrc-5.1.14.zip 63 | http://files.jrsoftware.org/is/5/issrc-5.2.0.zip 64 | http://files.jrsoftware.org/is/5/issrc-5.2.1.zip 65 | http://files.jrsoftware.org/is/5/issrc-5.2.2.zip 66 | http://files.jrsoftware.org/is/5/issrc-5.2.3.zip 67 | http://files.jrsoftware.org/is/5/issrc-5.3.0-beta.zip 68 | http://files.jrsoftware.org/is/5/issrc-5.3.1-beta.zip 69 | http://files.jrsoftware.org/is/5/issrc-5.3.2-beta.zip 70 | http://files.jrsoftware.org/is/5/issrc-5.3.3.zip 71 | http://files.jrsoftware.org/is/5/issrc-5.3.4.zip 72 | http://files.jrsoftware.org/is/5/issrc-5.3.5.zip 73 | http://files.jrsoftware.org/is/5/issrc-5.3.6.zip 74 | http://files.jrsoftware.org/is/5/issrc-5.3.7.zip 75 | http://files.jrsoftware.org/is/5/issrc-5.3.8.zip 76 | http://files.jrsoftware.org/is/5/issrc-5.3.9.zip 77 | http://files.jrsoftware.org/is/5/issrc-5.3.10.zip 78 | http://files.jrsoftware.org/is/5/issrc-5.3.11.zip 79 | http://files.jrsoftware.org/is/5/issrc-5.4.0.zip 80 | http://files.jrsoftware.org/is/5/issrc-5.4.1.zip 81 | http://files.jrsoftware.org/is/5/issrc-5.4.2.zip 82 | ); 83 | 84 | sub tag { 85 | my ($zip, $msg, $tag) = @_; 86 | system("$git rm -r ."); 87 | system("$unzip $zip"); 88 | system("$git add ."); 89 | system("$git commit -m '$msg'"); 90 | print("The error about the missing tag can be ignored.\n"); 91 | system("$git tag -d $tag"); 92 | system("$git tag $tag"); 93 | } 94 | 95 | system($wget, @sources); 96 | 97 | system("$git clone $giturl $srcdir"); 98 | chdir($srcdir); 99 | system("$git checkout $firstcommit"); 100 | system("$git checkout -b $branch"); 101 | 102 | for my $url (@sources) { 103 | my ($zipfile, $major, $minor, $micro) = ($url =~ /(issrc-([0-9])\.([0-9])\.([0-9]{1,2})(-beta)?\.zip)/); 104 | tag("../$zipfile", "$major.$minor.$micro", "is-$major\_$minor\_$micro"); 105 | } 106 | 107 | system("$git checkout master"); 108 | -------------------------------------------------------------------------------- /makestruct.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use FindBin; 5 | use lib "$FindBin::Bin"; 6 | use Cwd; 7 | use Getopt::Long; 8 | use ParserGenerator; 9 | use DelphiGrammar; 10 | 11 | my ($help, $git, $issrc, $version, $base, $stdtypes) = (undef, 'git', undef, undef, 'Setup::Inno::Struct', 0); 12 | GetOptions( 13 | "git=s" => \$git, 14 | "src=s" => \$issrc, 15 | "version=s" => \$version, 16 | "base=s" => \$base, 17 | "stdtypes" => \$stdtypes, 18 | "help" => \$help, 19 | ); 20 | if ($help || !defined($issrc) || !defined($version)) { 21 | print STDERR "Usage: makestruct.pl --src --version <1.2.3[u]> [--parser ] [--git ] [--base ] [--help]\n"; 22 | print STDERR " issrc the path to a clone of the innosetup source repository\n"; 23 | print STDERR " version the version number to check out prior to parsing (with u suffix for unicode mode)\n"; 24 | print STDERR " git the path to the git program (default: $git)\n"; 25 | print STDERR " base the name of the base class (default to $base)\n"; 26 | print STDERR " stdtypes add parsers for some standard Delphi types (like HKEY or DWORD)\n"; 27 | print STDERR " help for this help text\n"; 28 | exit 0; 29 | } 30 | 31 | my @types = qw( 32 | TSetupHeader 33 | TSetupLanguageEntry 34 | TSetupCustomMessageEntry 35 | TSetupPermissionEntry 36 | TSetupTypeEntry 37 | TSetupComponentEntry 38 | TSetupTaskEntry 39 | TSetupDirEntry 40 | TSetupFileEntry 41 | TSetupIconEntry 42 | TSetupIniEntry 43 | TSetupRegistryEntry 44 | TSetupDeleteEntry 45 | TSetupRunEntry 46 | TSetupFileLocationEntry 47 | ); 48 | 49 | my ($major, $minor, $micro, $unicode) = ($version =~ /^([0-9])\.([0-9])\.([0-9]{1,2})(u?)/); 50 | my $tag = sprintf('is-%u_%u_%u', $major, $minor, $micro); 51 | my $module = sprintf('Struct%u%u%02u%s', $major, $minor, $micro, $unicode ? 'u' : ''); 52 | my $output = "$module.pm"; 53 | my $package = "Setup::Inno::$module"; 54 | my $input = $issrc . '/Projects/Struct.pas'; 55 | 56 | sub checkout { 57 | my $dir = cwd(); 58 | chdir($issrc); 59 | system($git, 'checkout', $tag) == 0 || die("Can't check out tag $tag"); 60 | chdir($dir); 61 | } 62 | 63 | sub preprocess { 64 | my ($data) = @_; 65 | if ($unicode) { 66 | $$data =~ s/\{\$IFDEF UNICODE\}(.*?)(\{\$ELSE\}(.*?))?\{\$ENDIF\}/$1/gs; 67 | $$data =~ s/\{\$IFNDEF UNICODE\}(.*?)(\{\$ELSE\}(.*?))?\{\$ENDIF\}/$3/gs; 68 | } else { 69 | $$data =~ s/\{\$IFNDEF UNICODE\}(.*?)(\{\$ELSE\}(.*?))?\{\$ENDIF\}/$1/gs; 70 | $$data =~ s/\{\$IFDEF UNICODE\}(.*?)(\{\$ELSE\}(.*?))?\{\$ENDIF\}/$3/gs; 71 | } 72 | $$data =~ s#//[^\n]*\n#\n#gs; 73 | } 74 | 75 | sub generate { 76 | my ($out, $data, @types) = @_; 77 | 78 | my $re = DelphiGrammar->R('::array', 'ParserGenerator'); 79 | $re->read(\$data) || die("Error parsing input: $$"); 80 | my $value = ${$re->value}; 81 | 82 | for my $typename (@types) { 83 | my $type = $value->findtype($typename); 84 | if (defined($type)) { 85 | print($out $type->parserbyfield($value, $unicode)); 86 | } else { 87 | warn("Type $typename not found, ignoring"); 88 | } 89 | } 90 | } 91 | 92 | print("Checking out tag $tag...\n"); 93 | checkout(); 94 | 95 | print("Reading $input...\n"); 96 | open(my $in, '<', $input); 97 | my $data = do { local $/; <$in> } or die; 98 | undef($in); 99 | 100 | print("Preprocessing...\n"); 101 | preprocess(\$data); 102 | 103 | print("Writing to $output...\n"); 104 | open(my $out, '>', $output); 105 | 106 | print("Writing header...\n"); 107 | print($out "package $package;\n"); 108 | print($out "use strict;\n"); 109 | print($out "use base '$base';\n"); 110 | 111 | if ($stdtypes) { 112 | print $out <ReadLongWord(); 116 | my \$thigh = \$self->ReadLongWord(); 117 | my \$hnsecs = \$tlow | (\$thigh << 32); 118 | my \$secs = int(\$hnsecs / 10000000); 119 | my \$nsecs = (\$hnsecs - \$secs * 10000000) * 100; 120 | return DateTime->new(year => 1601, month => 1, day => 1, hour => 0, minute => 0, second => 0, nanosecond => 0)->add(seconds => \$secs, nanoseconds => \$nsecs); 121 | } 122 | sub HKEY { 123 | return shift->ReadLongWord(); 124 | } 125 | sub DWORD { 126 | return shift->ReadLongWord(); 127 | } 128 | sub TSHA1Digest { 129 | return shift->ReadByteArray(20); 130 | } 131 | sub TMD5Digest { 132 | return shift->ReadByteArray(16); 133 | } 134 | EOF 135 | } 136 | 137 | print("Generating parser for " . join(', ', @types) . "...\n"); 138 | generate($out, $data, @types); 139 | print($out "1;\n"); 140 | -------------------------------------------------------------------------------- /uninno.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Set to 1 to enable debugging mode 4 | our $DEBUG = 1; 5 | 6 | use strict; 7 | use warnings; 8 | use diagnostics; 9 | if ($DEBUG) { 10 | enable diagnostics; 11 | } else { 12 | disable diagnostics; 13 | } 14 | 15 | use FindBin; 16 | use lib "$FindBin::Bin"; 17 | use Setup::Inno; 18 | use Getopt::Long; 19 | use Text::Glob 'glob_to_regex'; 20 | use IO::File; 21 | use File::Basename; 22 | use File::Path 'make_path'; 23 | use File::Spec::Functions; 24 | 25 | $Text::Glob::strict_wildcard_slash = 0; 26 | 27 | # Enable autoflush on stdout 28 | $| = 1; 29 | 30 | my ($mode, $outdir, $strip, $help, $overwriteall, $password, $type) = ('extract', 'app', 0, 0, 0, undef, 'app'); 31 | GetOptions( 32 | "h" => \$help, 33 | "l" => sub { $mode = 'list' }, 34 | "e" => sub { $mode = 'extract'; $strip = 0 }, 35 | "x" => sub { $mode = 'extract'; $strip = 1 }, 36 | "d=s" => \$outdir, 37 | "p=s" => \$password, 38 | "t=s" => \$type, 39 | ); 40 | if ($help || @ARGV < 1) { 41 | print(STDERR "Usage: extract.pl [-l | -e | -x] [-h] [-d path] setup.exe [file.ext] [file.*] ...\n"); 42 | print(STDERR "Extracts files from InnoSetup setup.exe installers, loading setup-#.bin slices as appropriate.\n"); 43 | print(STDERR "You may optionally specify filenames or filename patterns to have only matching files extracted.\n"); 44 | print(STDERR "Options:\n"); 45 | print(STDERR "-h Show this help\n"); 46 | print(STDERR "-l List files instead of extracting\n"); 47 | print(STDERR "-e Extract files with full (relative) path (default action)\n"); 48 | print(STDERR "-x Extract files with stripped path\n"); 49 | print(STDERR "-d Specify output path (default: ./app/)\n"); 50 | print(STDERR "-p Specify the decryption password\n"); 51 | print(STDERR "-t Select which destination type is considered (default: app)\n"); 52 | print(STDERR " Known destination types: app, tmp, commonappdata, code, uninstexe, regsvrexe\n"); 53 | print(STDERR " Use 'all' to ignore the type\n"); 54 | exit(1); 55 | } 56 | 57 | my $filename = shift(@ARGV); 58 | my @patterns = @ARGV > 0 ? map({ glob_to_regex($_) } @ARGV) : glob_to_regex('*.*'); 59 | 60 | my $inno = Setup::Inno->new($filename); 61 | 62 | print("Installer version: " . $inno->Version . "\n"); 63 | print("Number of files: " . $inno->FileCount . "\n"); 64 | 65 | if (!$inno->VerifyPassword($password)) { 66 | print("WARNING: Invalid password specified. Extraction may fail when files are encrypted.\n"); 67 | } 68 | 69 | sub extract { 70 | my ($i, $file) = @_; 71 | printf("%u: %s %s %u %s %s%s...", $i, $file->{Name}, $file->{Type}, $file->{Size}, $file->{Date}->format_cldr('yyyy-MM-dd HH:mm:ss'), $file->{Compressed} ? 'C' : '', $file->{Encrypted} ? 'E' : ''); 72 | my $name = $file->{Name}; 73 | if ($strip) { 74 | $name =~ s#^.*?([^/]+)$#$1#; 75 | } else { 76 | $name =~ s#^[./]*##; 77 | } 78 | $name = catfile($outdir, $name); 79 | my $path = dirname($name); 80 | if (!stat($path)) { 81 | make_path($path); 82 | } 83 | my $writeone = $overwriteall; 84 | if (stat($name) && !$writeone) { 85 | print(" $name exists. Overwrite? [y/N/a]"); 86 | my $response = ; 87 | if ($response =~ /^[yY]/) { 88 | $writeone = 1; 89 | } elsif ($response =~ /^[aA]/) { 90 | $writeone = 1; 91 | $overwriteall = 1; 92 | } 93 | } else { 94 | $writeone = 1; 95 | } 96 | if ($writeone) { 97 | my $data = $inno->ReadFile($i, $password); 98 | my $output = IO::File->new($name, 'w') || die("Can't create $name: $@"); 99 | binmode($output); 100 | print($output $data); 101 | undef($output); 102 | print("done\n"); 103 | } else { 104 | print("ignored\n"); 105 | } 106 | } 107 | 108 | if ($mode eq 'list') { 109 | for (my $i = 0; $i < $inno->FileCount; $i++) { 110 | my $file = $inno->FileInfo($i); 111 | if ($type eq 'all' or $file->{Type} eq $type) { 112 | printf("%u: %s %s %u %s %s%s\n", $i, $file->{Name}, $file->{Type}, $file->{Size}, $file->{Date}->format_cldr('yyyy-MM-dd HH:mm:ss'), $file->{Compressed} ? 'C' : '', $file->{Encrypted} ? 'E' : ''); 113 | } 114 | } 115 | } elsif ($mode eq 'extract') { 116 | for my $i (map({ $inno->FindFiles($_) } @patterns)) { 117 | my $file = $inno->FileInfo($i); 118 | if (defined($file->{Type})) { 119 | if ($file->{Type} eq $type) { 120 | if ($DEBUG) { 121 | extract($i, $file); 122 | } else { 123 | eval { 124 | extract($i, $file); 125 | } or do { 126 | print("ERROR: $@"); 127 | } 128 | } 129 | } 130 | } elsif ($DEBUG) { 131 | use Data::Dumper; 132 | print("Unknown file:\n"); 133 | print(Dumper($file)); 134 | } 135 | } 136 | } 137 | 138 | --------------------------------------------------------------------------------