+Non-downloadable (or just no longer downloadable) fixed section
[captive.git] / src / install / acquire / captivemodid-print.pl
1 #! /usr/bin/perl
2 #
3 # $Id$
4 # Scan downloaded Microsoft files to build the .captivemodid.xml file core.
5 # Copyright (C) 2005 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use strict;
22 use warnings;
23
24 use Cwd;
25 use Carp qw(confess cluck);
26 use Digest::MD5 qw(md5_hex);
27 use File::Remove qw(remove);
28 use Getopt::Long;
29 use Data::Dumper;
30 require Config::IniHash;
31 require File::Basename;
32
33
34 confess if !$ENV{"HOME"};
35 my $tmp=$ENV{"HOME"}."/tmp/captivemodid";
36 confess "tmp '$tmp' too dangerous for tmpwatch(8)" if $tmp=~m{^/tmp\b} || $tmp=~m{^/var/tmp\b};
37 my $out;
38
39
40 my $opt_quiet;
41 die if !GetOptions(
42                 "q|quiet+",\$opt_quiet,
43                 );
44
45
46 our $dirnew=$ARGV[0];
47 do { $dirnew="."; warn "Defaulting ARGV[0] to: $dirnew"; } if !$dirnew;
48 our $out=$ARGV[1];
49 warn "No output md5 directory, not copying." if !$out;
50
51
52 my %type=(
53         "ntoskrnl.exe"=>"Kernel",
54         "ntkrnlpa.exe"=>"Kernel PA",
55         "ntkrnlmp.exe"=>"Kernel SMP",
56         "ntkrpamp.exe"=>"Kernel SMP PA",
57         "cdfs.sys"    =>"CD-ROM/iso-9660",
58         "fastfat.sys" =>"FastFAT/vfat",
59         "ntfs.sys"    =>"NTFS",
60         );
61
62 my %lang=(
63         "ar"=>"Arabic",
64         "hk"=>"Chinese (Hong Kong SAR)",
65         "cn"=>"Chinese (Simplified)",
66         "tw"=>"Chinese (Traditional)",
67         "cs"=>"Czech",
68         "da"=>"Danish",
69         "nl"=>"Dutch",
70         "en"=>"English",
71         "fi"=>"Finnish",
72         "fr"=>"French",
73         "de"=>"German",
74         "el"=>"Greek",
75         "he"=>"Hebrew",
76         "hu"=>"Hungarian",
77         "it"=>"Italian",
78         "ja"=>"Japanese",
79         "ko"=>"Korean",
80         "no"=>"Norwegian",
81         "pl"=>"Polish",
82         "br"=>"Portugese (Brazil)",
83         "pt"=>"Portugese (Portugal)",
84         "ru"=>"Russian",
85         "es"=>"Spanish",
86         "sv"=>"Swedish",
87         "tr"=>"Turkish",
88         "ara"=>"Arabic",
89         "chh"=>"Chinese (Hong Kong SAR)",
90         "chs"=>"Chinese (Simplified)",
91         "cht"=>"Chinese (Traditional)",
92         "csy"=>"Czech",
93         "dan"=>"Danish",
94         "nld"=>"Dutch",
95         "enu"=>"English",
96         "fin"=>"Finnish",
97         "fra"=>"French",
98         "deu"=>"German",
99         "ell"=>"Greek",
100         "heb"=>"Hebrew",
101         "hun"=>"Hungarian",
102         "ita"=>"Italian",
103         "jpn"=>"Japanese",
104         "kor"=>"Korean",
105         "nor"=>"Norwegian",
106         "plk"=>"Polish",
107         "ptb"=>"Portugese (Brazil)",
108         "ptg"=>"Portugese (Portugal)",
109         "rus"=>"Russian",
110         "esn"=>"Spanish",
111         "sve"=>"Swedish",
112         "trk"=>"Turkish",
113         );
114
115
116 sub name_valid($)
117 {
118 my($name)=@_;
119         $name=~tr/-/_/;
120         $name=lc $name;
121         return 0 if $name=~/[^a-z]ia64[^a-z]/;
122         return 1;
123 }
124
125 sub name_to_lang($)
126 {
127 my($name)=@_;
128
129         $name=~tr/-/_/;
130         $name=lc $name;
131         my $r;
132         while (my($subst,$long)=each(%lang)) {
133                 next if $name!~/[^a-z]$subst[^a-z]/;
134 # FIXME
135                 cluck "$name: $subst" if $r;
136                 $r=$long;
137                 }
138 # FIXME
139         cluck $name if !$r;
140         return $r;
141 }
142
143 my @checked_build=qw(
144         chk
145         debug
146         );
147
148 sub name_is_debug($)
149 {
150 my($name)=@_;
151
152         $name=~tr/-/_/;
153         $name=lc $name;
154         for my $subst (@checked_build) {
155                 return 1 if $name=~/[^a-z]$subst[^a-z]/;
156                 }
157         return 0;
158 }
159
160 my $ver_prefix="5.1.2600.";
161 my @stack_out;
162 my %md5sum_printed_global;
163
164 sub stack0_flush()
165 {
166         return if $opt_quiet;
167         # $md5sum_printed_local[$stacki]{md5}=1;
168         my @md5sum_printed_local;
169         while (my $stack=shift @stack_out) {
170                 my $hiding=1;
171                 for my $stacki (0..$#$stack) {
172                         my $this=$stack->[$stacki];
173                         $hiding=0 if !$md5sum_printed_local[$stacki]{$this->{"md5"}};
174                         next if $hiding;
175                         splice @md5sum_printed_local,$stacki,@md5sum_printed_local-$stacki,{$this->{"md5"}=>1};
176                         my $dupe_global=$md5sum_printed_global{$this->{"md5"}}++;
177                         my $print="";
178                         $print.=<<"HERE" if  $dupe_global;
179 <!-- @{[ $this->{"relname"} ]} md5="@{[ $this->{"md5"} ]}" -->
180 HERE
181                         $print.=<<"HERE" if !$dupe_global;
182 <!-- @{[ $this->{"relname"} ]} -->
183 <module type="@{[ $this->{"type"} ]}" length="@{[ $this->{"length"} ]}" priority="@{[ $this->{"priority"} ]}" md5="@{[ $this->{"md5"} ]}"
184                 id="@{[ $this->{"dist"} ]} @{[ !$this->{"is_debug"} ? "" : "Checked Build "]}@{[ $this->{"lang"} ]} $ver_prefix@{[ $this->{"ver"} ]} @{[ $this->{"this_name"} ]}" />
185 HERE
186                         $print=~s/^/"\t" x (1+$stacki)/egm;
187                         print $print;
188                         }
189                 }
190         print "\n";
191 }
192
193 our @stack;
194 my %md5sum_stored;
195 my $last_stack0_filename;
196
197 sub check($$)
198 {
199 my($filename_unused,$basename_orig)=@_;
200
201         return if !$type{lc $basename_orig};
202         my $final_type=$basename_orig;
203         my $final_name=$type{$final_type} or confess;
204         $final_type="ntoskrnl.exe" if $final_type=~/^nt.*[.]exe$/;
205         (my $basename_orig_=$basename_orig)=~s/.$/_/;
206         (my $basename0=$stack[0]->{"filename"})=~s{^.*/}{} or confess;
207         return if !name_valid($basename0);
208         stack0_flush() if $last_stack0_filename && $last_stack0_filename ne $stack[0]->{"filename"};
209         $last_stack0_filename=$stack[0]->{"filename"};
210         for my $stacki (0..$#stack) {
211                 my $this=$stack[$stacki];
212                 my $filename=$this->{"filename"} or confess;
213                 my $relname=$this->{"relname"} or confess;
214                 (my $basename=$filename)=~s{^.*/}{} or confess;
215                 my $expanded=$stack[$#stack]->{"filename"};
216                 my $this_name;
217                 my $type;
218                 if ($stacki==$#stack-1 && $basename eq $basename_orig_) {
219                         $this->{"type"} eq "cabinet" or confess;
220                         $type=$this->{"type"} or confess;
221                         $this_name="$final_name Cabinet";
222                         }
223                 elsif ($stacki<$#stack) {
224                         $type=$this->{"type"} or confess;
225                         $this_name="Cabinet";
226                         }
227                 else {
228                         $type=$final_type or confess;
229                         confess Dumper(\@stack) if $type eq "cabinet";
230                         $this_name=$final_name;
231                         }
232                 my $length=(stat $filename)[7] or confess;
233                 my $dist=$stack[0]->{"filename"};
234                 $dist=~s{^.*/}{} or confess;
235                 my $lang=name_to_lang $basename0;
236                 local *F;
237                 open F,$expanded or confess;
238                 my $buf="";
239                 my $buf2;
240                 my $ver;
241                 while (my $got=read F,$buf2,0x1000) {
242                         confess $expanded if !defined $got;
243                         last if !$got;
244                         confess $expanded if $got<0;
245                         confess $expanded if length($buf2)!=$got;
246                         $buf.=$buf2;
247                         $ver=$buf;
248                         $ver=~s/\x00\x00+/!/g;
249                         $ver=~tr/\x00//d;
250                         $ver=~tr/!/\x00/;
251                         last if $ver=~s/^.*\x00\Q$ver_prefix\E(\d+)[ \x00].*$/$1/s;
252                         $ver=undef();
253                         $buf=substr($buf,-0x100);
254                         }
255                 confess $expanded if !$ver;
256                 close F or confess;
257                 $ver=~/^\d+$/ or confess "$expanded: $ver";
258                 my $pri=sprintf "510%04u00",$ver;
259                 $pri+=90;
260                 $pri-=10 if $lang ne "English";
261                 $pri-=20 if $basename_orig=~/^ntkrnlpa/;
262                 $pri-=40 if $basename_orig=~/^ntkrnlmp/;
263                 $pri-=60 if $basename_orig=~/^ntkrpamp/;
264                 $pri+=5000000 if my $is_debug=name_is_debug $basename0;
265                 local *F;
266                 open F,$filename or confess;
267                 my $md5obj=Digest::MD5->new();
268                 $md5obj->addfile(*F);
269                 close F or confess;
270                 my $md5sum=lc $md5obj->hexdigest();
271                 if (!$md5sum_stored{$md5sum}++ && $out && $stacki==$#stack) {
272                         my $dir="$out/$final_type";
273
274                         mkdir_checked($dir) if !-d $dir;
275                         spawn("cp -p '$filename' '$dir/$md5sum'");
276                         }
277
278                 my %new=(
279                         %$this,
280                         "type"=>$type,
281                         "length"=>$length,
282                         "priority"=>$pri,
283                         "md5"=>$md5sum,
284                         "dist"=>$dist,
285                         "lang"=>$lang,
286                         "ver"=>$ver,
287                         "this_name"=>$this_name,
288                         "is_debug"=>$is_debug,
289                         );
290                 # Highest priority/version of all the files in the cabinet:
291                 for (qw(ver priority)) {
292                         next if !$this->{$_};
293                         $new{$_}=$this->{$_} if $new{$_}<$this->{$_};
294                         delete $this->{$_} if $this->{$_}<$new{$_};
295                         }
296                 do { cluck Dumper($this,\%new) if $new{$_} ne $this->{$_}; } for keys(%$this);
297                 %$this=%new;
298                 }
299         # It is intentional to copy only the references - to update their "ver".
300         push @stack_out,[@stack];
301 }
302
303 sub spawn($)
304 {
305 my($command)=@_;
306
307         my $code=system "($command)".' >&2';
308         confess "$code: $command" if $code;
309 }
310
311 sub rm_rf($)
312 {
313 my($dir)=@_;
314
315         return if !-e $dir;
316         # Do not: Can't remove directory xyzzy: Directory not empty
317         #         -r--r--r-- xyzzy
318         #         remove \1,$dir or confess "$dir: $!";
319         spawn "rm -rf '$dir'";
320 }
321
322 sub mkdir_checked
323 {
324 my(@dirs)=@_;
325
326         for (@dirs) {
327                 mkdir $_ or confess "$_: $!";
328                 }
329 }
330
331
332 our $depth=-1;
333
334 sub prep()
335 {
336         confess if $dirnew;
337         $dirnew="$tmp/$depth";
338         chdir "/" or confess;
339         rm_rf $dirnew;
340         mkdir_checked $dirnew;
341         chdir $dirnew or confess "chdir: $dirnew";
342         return $dirnew;
343 }
344
345 sub relname($)
346 {
347 my($filename)=@_;
348
349         return $filename if $filename=~s{^\Q$tmp\E/\d+/}{};
350         (my $basename=$filename)=~s{^.*/}{} or confess;
351         return $basename;
352 }
353
354 my %unknown;
355 sub process(;$$);
356 sub process(;$$)
357 {
358 my($ref,$reftype)=@_;
359
360         my $dirname=$dirnew or confess;
361         $dirnew=undef();
362         local $depth=$depth+1;
363         my @stack_orig=@stack;
364         local @stack;
365
366         # Handle so-called 'mspatch'.
367         # Look for Product/Technology: Platform SDK
368         # Item like: Microsoft Windows SDK
369         #       http://www.microsoft.com/downloads/details.aspx?FamilyID=2297bdc9-b5ae-4b8a-b601-eef54a52867a&DisplayLang=en
370         #       http://download.microsoft.com/download/0/7/3/073d42b8-e2ba-4293-ad97-28365dc2d655/6.0.5270.0.9.WindowsSDK_Vista_idw.DVD.Rel_Update.img
371         #       Setup/WinSDK-SDK_MSI_SMP-common.0.cab 
372         #       APATCH 5.1.2600.0 Patch Application Utility
373         # Do not use: Windows Installer SDK (x86): APATCH 1.94 Patch Application Utility
374         # as it will break on some files (incompatible with some new features).
375         # You need proper Wine setup incl. its binary filetype registration.
376         if (-e "$dirname/_sfx_manifest_") {
377                 Config::IniHash::ReadINI("_sfx_manifest_",
378                                 # The only way how to read it in the original order:
379                                 "forValue"=>sub {
380                                         my($dest_raw,$src_raw,$sectionname,$inihashref)=@_;
381                                         return if $sectionname ne "Deltas";
382                                         $dest_raw=~tr{\\}{/};
383                                         $src_raw=~tr{\\}{/};
384                                         my $dest=($dest_raw=~/^\s*"([^"]+)"\s*$/)[0] or confess;
385                                         my($patch,$src)=($src_raw=~/^\s*"([^"]+)"\s*,\s*"([^"]+)"\s*$/) or confess;
386                                         my $destdir=File::Basename::dirname($dest);
387                                         cluck "$dirname/$patch" if !-r "$dirname/$patch";
388                                         cluck "$dirname/$src" if !-r "$dirname/$src";
389                                         # Not &mkdir: Take care of already existing directories and ... "-p".
390                                         spawn "mkdir -p '$dirname/$destdir'";
391                                         rm_rf "$dirname/$dest";
392                                         spawn "true;(apatch '$dirname/$patch' '$dirname/$src' '$dirname/$dest' &>/dev/null)";
393                                         return;
394                                         },
395                                 );
396                 }
397
398         local *FIND;
399         open FIND,"find '$dirname' -type f|sort|" or confess $dirname;
400         local $_;
401         while (<FIND>) {
402                 next if $_ eq "";
403                 print STDERR "." if !$depth;
404                 chomp;
405                 my $filename=$_;
406                 $filename=~m{^/} or confess $filename;
407                 (my $basename=$filename)=~s{^.*/}{} or confess;
408                 -r $filename or do { cluck Dumper(\@stack,$filename); system("ls -l '$filename'"); };
409                 $$ref=$reftype if $ref;
410                 my $this={
411                         "filename"=>$filename,
412                         "relname"=>relname($filename),
413                         };
414                 @stack=(@stack_orig,$this);
415
416                 if ($basename=~/[.]_p$/) {
417                         do { cluck "Missing $_" if !-e $_; } for "_sfx_manifest_";
418                         next;
419                         }
420
421                 check $filename,$basename;
422
423                 local *MIME;
424                 open MIME,"file -b -i '$filename'|" or confess;
425                 my $mime=do { local $/; <MIME> or confess; };
426                 close MIME or confess;
427                 chomp $mime;
428                 next if $mime=~m{^text/};
429                 next if $mime=~m{^image/};
430                 next if $mime=~m{^audio/};
431                 next if $mime eq 'application/x-empty';
432                 next if $mime eq 'application/msaccess';
433
434                 local *ID;
435                 open ID,"file -b '$filename'|" or confess;
436                 my $id=do { local $/; <ID> or confess; };
437                 close ID or confess;
438                 chomp $id;
439                 next if $id eq 'Microsoft Office Document';
440                 next if $id=~/^PDF document, version 1[.]\d+$/;
441                 next if $id eq 'Rich Text Format data, version 1,';
442                 next if $id=~/^x86 boot sector, /;
443                 next if $id eq 'data';
444                 next if $id eq 'MZ executable for MS-DOS';
445                 next if $id eq 'XML document text';
446                 next if $id eq 'MS Windows HtmlHelp Data';
447                 next if $id eq 'MPEG sequence';
448                 next if $id=~/^MSVC program database ver /;
449                 next if $id eq 'Lotus 1-2-3';
450                 next if $id eq 'MS Windows Help Data';
451                 next if $id=~/^Macromedia Flash data, /;
452                 next if $id eq 'Microsoft ASF'; # .wmv
453                 next if $id eq 'GLF_BINARY_LSB_FIRST';  # .hlp
454                 next if $id=~/^Sendmail frozen configuration /;
455                 next if $id eq 'Assembler source';
456                 next if $id=~/^DBase 3 data file/;
457                 next if $id=~/^Macintosh HFS Extended version /;
458                 next if $id=~/^MPEG ADTS, /;    # .txt?,.hlp?
459                 next if $id eq 'MS-DOS executable (COM)';
460                 next if $id eq 'NE executable for MS Windows 3.x (driver)';
461                 next if $id eq 'TrueType font data';
462                 next if $id eq 'Windows NT registry file';
463                 next if $id eq 'lif file';      # .sig?
464                 next if $id=~/^Infocom game data /;     # .chm?
465                 next if $id=~/^Sun disk label /;        # .dl_?
466                 next if $id=~/^compiled Java class data, /;
467                 next if $id=~/^Minix filesystem/;       # .dl_?
468                 next if $id eq 'DBase 3 index file';    # .nls?
469                 next if $id=~/^Macintosh MFS data /;    # .cp_?
470                 next if $id eq 'ACB archive data';      # .cat?
471                 next if $id eq 'COM executable for MS-DOS';     # .edb?
472                 next if $id eq 'SysEx File - Gulbransen';       # .edb?
473                 next if $id eq 'LE executable for MS Windows (VxD)';    # .386?
474
475                 # Do not:
476                 #next if $id=~/^PE executable for MS Windows .* 32-bit$/;       # TODO
477
478                 if ($id eq 'PE executable for MS Windows (GUI) Intel 80386 32-bit, InnoSetup self-extracting archive') {
479                         # FIXME: http://innounp.sourceforge.net/
480                         next;
481                         }
482                 if (0
483                                 || $id=~/^Microsoft Cabinet archive data, \d+ byte(?:s)?, \d+ file(?:s)?$/
484                                 || $id eq 'PE executable for MS Windows (GUI) Intel 80386 32-bit, MS CAB-Installer self-extracting archive'
485                                 # Catch all unidentified flying cabinets:
486                                 || $id=~/^PE executable for MS Windows .* 32-bit$/
487                                 ) {
488                         # Acceleration: Spawning cabextract(1) is slow, try to find "MSCF" first.
489                         local *CAB;
490                         open CAB,$filename or confess $filename;
491                         my $buf="";
492                         my $buf2;
493                         my $mscf;       # magic
494                         while (my $got=read CAB,$buf2,0x1000) {
495                                 confess $filename if !defined $got;
496                                 last if !$got;
497                                 confess $filename if $got<0;
498                                 confess $filename if length($buf2)!=$got;
499                                 $buf.=$buf2;
500                                 last if $mscf=($buf=~/MSCF/s);
501                                 $buf=substr($buf,-4);
502                                 }
503                         close CAB or confess;
504                         next if !$mscf;
505                         prep();
506                         spawn q{cabextract -q '}.$filename.q{' 2>&1|grep -v ': \(}.join(q{\|},
507                                         q{WARNING; possible [0-9]* extra bytes at end of file.},
508                                         q{no valid cabinets found},
509                                         q{checksum error},
510                                         q{error in CAB data format},
511                                         ).q{\)$';true};
512                         process(\$this->{"type"},"cabinet");
513                         next;
514                         }
515                 if (0
516                                 || $id eq 'PE executable for MS Windows (GUI) Intel 80386 32-bit, ZIP self-extracting archive (WinZip)'
517                                 || $id=~/^Zip archive data, /
518                                 ) {
519                         prep();
520                         spawn "unzip -q '$filename'";
521                         process();
522                         next;
523                         }
524                 # non-"32-bit" - amd64 in fact but not detected by file(1).
525                 # Really.
526                 next if $id=~/^PE executable for MS Windows/;
527                 if ($id=~m{^gzip compressed data, was "\S+", from Win/32}) {
528                         prep();
529                         spawn "gzip -d <'$filename' >'$basename'";
530                         process();
531                         next;
532                         }
533                 if ($id eq 'tar archive') {
534                         prep();
535                         spawn "tar xf '$filename'";
536                         process();
537                         next;
538                         }
539                 warn "$filename: $mime - $id" if !$unknown{$mime,$id}++;
540                 }
541         close FIND or confess;
542         $dirnew=undef();
543 }
544
545
546 $dirnew=getcwd()."/".$dirnew if $dirnew!~m{^/};
547 $out   =getcwd()."/".$out    if $out && $out!~m{^/};
548 rm_rf $tmp;
549 mkdir_checked $tmp;
550 if ($out) {
551         rm_rf $out;
552         mkdir_checked $out;
553         }
554 $|=1;
555 print <<"HERE";
556 <?xml version="1.0" encoding="UTF-8"?>
557 <!-- @{[ '$' ]}Id@{[ '$' ]}
558 HERE
559 print <<'HERE';
560  - Database of ids for Captive-compatible W32 binary modules
561  - Copyright (C) 2003-2005 Jan Kratochvil <project-captive@jankratochvil.net>
562  - 
563  - This program is free software; you can redistribute it and/or modify
564  - it under the terms of the GNU General Public License as published by
565  - the Free Software Foundation; exactly version 2 of June 1991 is required
566  - 
567  - This program is distributed in the hope that it will be useful,
568  - but WITHOUT ANY WARRANTY; without even the implied warranty of
569  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
570  - GNU General Public License for more details.
571  - 
572  - You should have received a copy of the GNU General Public License
573  - along with this program; if not, write to the Free Software
574  - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
575  -->
576 <!--
577  - XP No Service Pack                           5.1.2600.0
578  - Q317277_WXP_SP1_x86 xpclnt_qfe.010827-1803   5.1.2600.31
579  - Q811493_WXP_SP2 2600.xpclnt_qfe.021108-2107  5.1.2600.108
580  - XP Service Pack 1/1a xpsp1.020828-1920       5.1.2600.1106
581  - Q811493_WXP_SP2 2600.xpsp2.030422-1633       5.1.2600.1151
582  -
583  - 510110690:
584  -        90->{ntoskrnl +80}+{English +10}
585  -    1106->5.1.2600.{1106}
586  -   0->{0=Free,5=Checked}
587  - 51->{5}.{1}
588  - ntoskrnl
589  -          ntoskrnl +80 (Uniprocessor 4GB or less)
590  -          ntkrnlpa +60 (Uniprocessor >4GB extended addressing support)
591  -          ntkrnlmp +40 (Multiprocessor 4GB or less)
592  -          ntkrpamp +20 (Multiprocessor >4GB extended addressing support)
593  - English           +10
594  -->
595 <modid>
596
597
598         <!-- Non-downloadable (or just no longer downloadable) fixed section: { -->
599
600
601         <!-- MS-Windows XP (No Service Pack) Checked Build English 5.1.2600.0 -->
602                 <!-- ntoskrnl.ex_ -->
603                 <module type="cabinet" length="1229871" priority="515000090" md5="ffd9de8245c9a2239c5bdccd25c301bc"
604                                 id="(No Service Pack) Checked Build English 5.1.2600.0 Kernel Cabinet" />
605                 <!-- ntkrnlmp.ex_ -->
606                 <module type="cabinet" length="1230619" priority="515000050" md5="1e12693d52c12e4f9be95e278d8abcd3"
607                                 id="(No Service Pack) Checked Build English 5.1.2600.0 Kernel SMP Cabinet" />
608                 <!-- fastfat.sy_ -->
609                 <module type="cabinet" length="92500" priority="515000090" md5="a1afe5e343bc0d922c21328c3d250f41"
610                                 id="(No Service Pack) Checked Build English 5.1.2600.0 FastFAT/vfat Cabinet" />
611                 <!-- cdfs.sy_ -->
612                 <module type="cabinet" length="43821" priority="515000090" md5="44f9d65d8045a18c14dfde732324f361"
613                                 id="(No Service Pack) Checked Build English 5.1.2600.0 CD-ROM/iso-9660 Cabinet" />
614                 <!-- ntoskrnl.exe -->
615                 <module type="ntoskrnl.exe" length="3396096" priority="515000090" md5="aafc4863e2aba5287d84a3da05105a92"
616                                 id="(No Service Pack) Checked Build English 5.1.2600.0 Kernel" />
617                 <!-- ntkrnlmp.exe -->
618                 <module type="ntoskrnl.exe" length="3396608" priority="515000050" md5="d35fc3d2e33306b1ad660ab7b723a924"
619                                 id="(No Service Pack) Checked Build English 5.1.2600.0 Kernel SMP" />
620                 <!-- ntfs.sys -->
621                 <module type="ntfs.sys" length="809344" priority="515000090" md5="962efb6be87a642eb5d0d4b381d3786b"
622                                 id="(No Service Pack) Checked Build English 5.1.2600.0 NTFS" />
623                 <!-- fastfat.sys -->
624                 <module type="fastfat.sys" length="227328" priority="515000090" md5="595a1602383e6c18e233aaa97fc3ba2e"
625                                 id="(No Service Pack) Checked Build English 5.1.2600.0 FastFAT/vfat" />
626                 <!-- cdfs.sys -->
627                 <module type="cdfs.sys" length="114688" priority="515000090" md5="5a75583c08d709d5ed926e7dfed5c09c"
628                                 id="(No Service Pack) Checked Build English 5.1.2600.0 CD-ROM/iso-9660" />
629
630         <!-- MS-Windows XP (No Service Pack) 5.1.2600.0 -->
631                 <!-- fastfat.sy_ -->
632                 <module type="cabinet" length="76742" priority="510000090" md5="2c0b027760858b4b6ff823deb61b1785"
633                                 id="(No Service Pack) 5.1.2600.0 FastFAT/vfat Cabinet" />
634                 <!-- cdfs.sy_ -->
635                 <module type="cabinet" length="34873" priority="510000090" md5="b6d7f84df4431f79173574b873a9edf0"
636                                 id="(No Service Pack) 5.1.2600.0 CD-ROM/iso-9660 Cabinet" />
637                 <!-- ntfs.sys -->
638                 <module type="ntfs.sys" length="533504" priority="510000090" md5="70fae0dcfdfaa0838d6778fca028ce01"
639                                 id="(No Service Pack) 5.1.2600.0 NTFS" />
640                 <!-- fastfat.sys -->
641                 <module type="fastfat.sys" length="144768" priority="510000090" md5="998bbf32a142910b5e539df4225df892"
642                                 id="(No Service Pack) 5.1.2600.0 FastFAT/vfat" />
643                 <!-- cdfs.sys -->
644                 <module type="cdfs.sys" length="62208" priority="510000090" md5="bab95bbefd0676eab2dc02cf88c99fc5"
645                                 id="(No Service Pack) 5.1.2600.0 CD-ROM/iso-9660" />
646
647         <!-- MS-Windows XP (No Service Pack) English 5.1.2600.0 -->
648                 <!-- ntoskrnl.ex_ -->
649                 <module type="cabinet" length="969331" priority="510000090" md5="d76e823d8b9004ce658accae0235cbd7"
650                                 id="(No Service Pack) English 5.1.2600.0 Kernel Cabinet" />
651                 <!-- ntkrnlmp.ex_ -->
652                 <module type="cabinet" length="938895" priority="510000050" md5="a12abfbc47263fd060f81dad2d53d655"
653                                 id="(No Service Pack) English 5.1.2600.0 Kernel SMP Cabinet" />
654                 <!-- ntoskrnl.exe -->
655                 <module type="ntoskrnl.exe" length="1982208" priority="510000090" md5="a29222d5281056e497408fcc9062f749"
656                                 id="(No Service Pack) English 5.1.2600.0 Kernel" />
657                 <!-- ntkrnlmp.exe -->
658                 <module type="ntoskrnl.exe" length="1897984" priority="510000050" md5="5e9003146793d4a8d2b46c7414965daf"
659                                 id="(No Service Pack) English 5.1.2600.0 Kernel SMP" />
660
661         <!-- MS-Windows XP (No Service Pack) German 5.1.2600.0 -->
662                 <!-- ntoskrnl.exe -->
663                 <module type="ntoskrnl.exe" length="1984512" priority="510000080" md5="3ba950b403060180606235bbb955a315"
664                                 id="(No Service Pack) German 5.1.2600.0 Kernel" />
665
666
667         <module type="ext2fsd.sys" length="171325" priority="100" md5="39af1cc9b68c662d29b05547342546c7"
668                         id="ext2 Filesystem v0.10a by http://sys.xiloo.com Checked Build English" />
669         <module type="ext2fsd.sys" length="59514" priority="50" md5="8803c8f4c535d6a58606bcd0ff3f77e9"
670                         id="ext2 Filesystem v0.10a by http://sys.xiloo.com English" />
671
672
673         <!-- xpsp1_en_x86_chk.exe -->
674         <module type="cabinet" length="160852568" priority="515110600" md5="ed5c40cab78513084f9a11cdd3a25361"
675                         id="Service Pack 1 Checked Build English Cabinet" />
676         <!-- xpsp1_en_x86.exe -->
677         <module type="cabinet" length="140440152" priority="510110600" md5="ecf625f6563dfcaeeecc4b1481899d4b"
678                         id="Service Pack 1 English Cabinet" />
679
680
681         <!-- Non-downloadable (or just no longer downloadable) fixed section: } -->
682
683
684 HERE
685 process;
686 stack0_flush() if $last_stack0_filename;
687 rm_rf $tmp;
688 print STDERR "\n";
689 print <<'HERE';
690
691 </modid>
692 HERE