4 # Scan downloaded Microsoft files to build the .captivemodid.xml file core.
5 # Copyright (C) 2005 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
25 use Carp qw(confess cluck);
26 use Digest::MD5 qw(md5_hex);
27 use File::Remove qw(remove);
30 require Config::IniHash;
31 require File::Basename;
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};
42 "q|quiet+",\$opt_quiet,
47 do { $dirnew="."; warn "Defaulting ARGV[0] to: $dirnew"; } if !$dirnew;
49 warn "No output md5 directory, not copying." if !$out;
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",
64 "hk"=>"Chinese (Hong Kong SAR)",
65 "cn"=>"Chinese (Simplified)",
66 "tw"=>"Chinese (Traditional)",
82 "br"=>"Portugese (Brazil)",
83 "pt"=>"Portugese (Portugal)",
89 "chh"=>"Chinese (Hong Kong SAR)",
90 "chs"=>"Chinese (Simplified)",
91 "cht"=>"Chinese (Traditional)",
107 "ptb"=>"Portugese (Brazil)",
108 "ptg"=>"Portugese (Portugal)",
121 return 0 if $name=~/[^a-z]ia64[^a-z]/;
132 while (my($subst,$long)=each(%lang)) {
133 next if $name!~/[^a-z]$subst[^a-z]/;
135 cluck "$name: $subst" if $r;
143 my @checked_build=qw(
154 for my $subst (@checked_build) {
155 return 1 if $name=~/[^a-z]$subst[^a-z]/;
160 my $ver_prefix="5.1.2600.";
162 my %md5sum_printed_global;
166 return if $opt_quiet;
167 # $md5sum_printed_local[$stacki]{md5}=1;
168 my @md5sum_printed_local;
169 while (my $stack=shift @stack_out) {
171 for my $stacki (0..$#$stack) {
172 my $this=$stack->[$stacki];
173 $hiding=0 if !$md5sum_printed_local[$stacki]{$this->{"md5"}};
175 splice @md5sum_printed_local,$stacki,@md5sum_printed_local-$stacki,{$this->{"md5"}=>1};
176 my $dupe_global=$md5sum_printed_global{$this->{"md5"}}++;
178 $print.=<<"HERE" if $dupe_global;
179 <!-- @{[ $this->{"relname"} ]} md5="@{[ $this->{"md5"} ]}" -->
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"} ]}" />
186 $print=~s/^/"\t" x (1+$stacki)/egm;
195 my $last_stack0_filename;
199 my($filename_unused,$basename_orig)=@_;
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"};
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";
223 elsif ($stacki<$#stack) {
224 $type=$this->{"type"} or confess;
225 $this_name="Cabinet";
228 $type=$final_type or confess;
229 confess Dumper(\@stack) if $type eq "cabinet";
230 $this_name=$final_name;
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;
237 open F,$expanded or confess;
241 while (my $got=read F,$buf2,0x1000) {
242 confess $expanded if !defined $got;
244 confess $expanded if $got<0;
245 confess $expanded if length($buf2)!=$got;
248 $ver=~s/\x00\x00+/!/g;
251 last if $ver=~s/^.*\x00\Q$ver_prefix\E(\d+)[ \x00].*$/$1/s;
253 $buf=substr($buf,-0x100);
255 confess $expanded if !$ver;
257 $ver=~/^\d+$/ or confess "$expanded: $ver";
258 my $pri=sprintf "510%04u00",$ver;
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;
266 open F,$filename or confess;
267 my $md5obj=Digest::MD5->new();
268 $md5obj->addfile(*F);
270 my $md5sum=lc $md5obj->hexdigest();
271 if (!$md5sum_stored{$md5sum}++ && $out && $stacki==$#stack) {
272 my $dir="$out/$final_type";
274 mkdir_checked($dir) if !-d $dir;
275 spawn("cp -p '$filename' '$dir/$md5sum'");
287 "this_name"=>$this_name,
288 "is_debug"=>$is_debug,
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{$_};
296 do { cluck Dumper($this,\%new) if $new{$_} ne $this->{$_}; } for keys(%$this);
299 # It is intentional to copy only the references - to update their "ver".
300 push @stack_out,[@stack];
307 my $code=system "($command)".' >&2';
308 confess "$code: $command" if $code;
316 # Do not: Can't remove directory xyzzy: Directory not empty
318 # remove \1,$dir or confess "$dir: $!";
319 spawn "rm -rf '$dir'";
327 mkdir $_ or confess "$_: $!";
337 $dirnew="$tmp/$depth";
338 chdir "/" or confess;
340 mkdir_checked $dirnew;
341 chdir $dirnew or confess "chdir: $dirnew";
349 return $filename if $filename=~s{^\Q$tmp\E/\d+/}{};
350 (my $basename=$filename)=~s{^.*/}{} or confess;
358 my($ref,$reftype)=@_;
360 my $dirname=$dirnew or confess;
362 local $depth=$depth+1;
363 my @stack_orig=@stack;
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:
380 my($dest_raw,$src_raw,$sectionname,$inihashref)=@_;
381 return if $sectionname ne "Deltas";
382 $dest_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)";
399 open FIND,"find '$dirname' -type f|sort|" or confess $dirname;
403 print STDERR "." if !$depth;
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;
411 "filename"=>$filename,
412 "relname"=>relname($filename),
414 @stack=(@stack_orig,$this);
416 if ($basename=~/[.]_p$/) {
417 do { cluck "Missing $_" if !-e $_; } for "_sfx_manifest_";
421 check $filename,$basename;
424 open MIME,"file -b -i '$filename'|" or confess;
425 my $mime=do { local $/; <MIME> or confess; };
426 close MIME or confess;
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';
435 open ID,"file -b '$filename'|" or confess;
436 my $id=do { local $/; <ID> or confess; };
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?
476 #next if $id=~/^PE executable for MS Windows .* 32-bit$/; # TODO
478 if ($id eq 'PE executable for MS Windows (GUI) Intel 80386 32-bit, InnoSetup self-extracting archive') {
479 # FIXME: http://innounp.sourceforge.net/
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$/
488 # Acceleration: Spawning cabextract(1) is slow, try to find "MSCF" first.
490 open CAB,$filename or confess $filename;
494 while (my $got=read CAB,$buf2,0x1000) {
495 confess $filename if !defined $got;
497 confess $filename if $got<0;
498 confess $filename if length($buf2)!=$got;
500 last if $mscf=($buf=~/MSCF/s);
501 $buf=substr($buf,-4);
503 close CAB or confess;
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},
510 q{error in CAB data format},
512 process(\$this->{"type"},"cabinet");
516 || $id eq 'PE executable for MS Windows (GUI) Intel 80386 32-bit, ZIP self-extracting archive (WinZip)'
517 || $id=~/^Zip archive data, /
520 spawn "unzip -q '$filename'";
524 # non-"32-bit" - amd64 in fact but not detected by file(1).
526 next if $id=~/^PE executable for MS Windows/;
527 if ($id=~m{^gzip compressed data, was "\S+", from Win/32}) {
529 spawn "gzip -d <'$filename' >'$basename'";
533 if ($id eq 'tar archive') {
535 spawn "tar xf '$filename'";
539 warn "$filename: $mime - $id" if !$unknown{$mime,$id}++;
541 close FIND or confess;
546 $dirnew=getcwd()."/".$dirnew if $dirnew!~m{^/};
547 $out =getcwd()."/".$out if $out && $out!~m{^/};
556 <?xml version="1.0" encoding="UTF-8"?>
557 <!-- @{[ '$' ]}Id@{[ '$' ]}
560 - Database of ids for Captive-compatible W32 binary modules
561 - Copyright (C) 2003-2005 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
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
584 - 90->{ntoskrnl +80}+{English +10}
585 - 1106->5.1.2600.{1106}
586 - 0->{0=Free,5=Checked}
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)
598 <!-- Non-downloadable (or just no longer downloadable) fixed section: { -->
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" />
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" />
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" />
621 <module type="ntfs.sys" length="809344" priority="515000090" md5="962efb6be87a642eb5d0d4b381d3786b"
622 id="(No Service Pack) Checked Build English 5.1.2600.0 NTFS" />
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" />
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" />
630 <!-- MS-Windows XP (No Service Pack) 5.1.2600.0 -->
632 <module type="cabinet" length="76742" priority="510000090" md5="2c0b027760858b4b6ff823deb61b1785"
633 id="(No Service Pack) 5.1.2600.0 FastFAT/vfat Cabinet" />
635 <module type="cabinet" length="34873" priority="510000090" md5="b6d7f84df4431f79173574b873a9edf0"
636 id="(No Service Pack) 5.1.2600.0 CD-ROM/iso-9660 Cabinet" />
638 <module type="ntfs.sys" length="533504" priority="510000090" md5="70fae0dcfdfaa0838d6778fca028ce01"
639 id="(No Service Pack) 5.1.2600.0 NTFS" />
641 <module type="fastfat.sys" length="144768" priority="510000090" md5="998bbf32a142910b5e539df4225df892"
642 id="(No Service Pack) 5.1.2600.0 FastFAT/vfat" />
644 <module type="cdfs.sys" length="62208" priority="510000090" md5="bab95bbefd0676eab2dc02cf88c99fc5"
645 id="(No Service Pack) 5.1.2600.0 CD-ROM/iso-9660" />
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" />
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" />
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" />
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" />
681 <!-- Non-downloadable (or just no longer downloadable) fixed section: } -->
686 stack0_flush() if $last_stack0_filename;