Maxis did something weird and crammed a bunch of BMP files together into a single "archive" format. All the textures are in these four files:
skycool.bmp and skygray.bmp are red herrings: they are not referenced anywhere within SimCopter. But they do look cool. Maybe there was a planned "choose your fogginess" option where these would have fit into a menu somewhere (View Depth or something).
All integers are Little-Endian (Intel) unless otherwise noted
Palette data is stored in the CMAP block of .max files: 768 bytes R,G,B. Make sure to convert to BGR0 if writing a BMP
Structure: ===HEADER=== int32: Total file size int32: Always 0 int32: Number of images stored in the File int32: Number of Resolution blocks to follow RESOLUTIONS (N times, see above): int32: x int32: y int32: attrib (name, int32, or 0) ===TEXTURE=== int32: X Dimension int32: Y Dimension of Images int32: Always 0 Y int32s: offset of start of each Row in subsequent data IMAGE DATA (bytes), each image is X by Y bytes
Here is a Perl script that will take a 768-byte palette as arg0 and a packed-bmp as arg1, and spit out (valid) BMP files with attached palette, 1 per texture in original file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | #!/usr/bin/perl use strict; sub r_v { my $fp = shift; my $buf; my $read_back = read ($fp, $buf, 4); if ($read_back != 4) { die "Short read on input: expected 4 bytes, got $read_back\n"; } return unpack ('V', $buf); } my $fp; my @cmap; my @x; my @y; my @tag; my $temp; print "Reading colormap."; open ($fp, $ARGV[0]) or die "can't open $ARGV[0]: $!\n"; binmode($fp); for (my $i = 0; $i < 256; $i ++) { read ($fp, $temp, 1); $cmap[$i][2] = $temp; read ($fp, $temp, 1); $cmap[$i][1] = $temp; read ($fp, $temp, 1); $cmap[$i][0] = $temp; } close($fp); print "All done: now opening packed bmp\n"; open ($fp, $ARGV[1]) or die "can't open $ARGV[1]: $!\n"; binmode($fp); $temp = r_v($fp); print "Size: $temp\n"; $temp = r_v($fp); print "Expect 0: $temp\n"; my $num_images = r_v($fp); print "** $num_images textures in file**\n"; $temp = r_v($fp); print "Looping to pick up $temp meta-attribs\n"; for (my $i = 0; $i < $num_images; $i ++) { $x[$i] = r_v($fp); $y[$i] = r_v($fp); $tag[$i] = r_v($fp); } print "OK! Beginning image extract.\n"; for (my $i = 0; $i < $num_images; $i ++) { print ". image " . $i . "\n"; my $local_x = r_v($fp); if ($x[$i] != $local_x) { print "WARNING: Mismatch between Image Width: $x[$i] vs $local_x\n"; } my $local_y = r_v($fp); if ($y[$i] != $local_y) { print "WARNING: Mismatch between Image Width: $y[$i] vs $local_y\n"; } $temp = r_v($fp); print "Expect 0: $temp\n"; my $offset = 0; for (my $j = 0; $j < $local_y; $j ++) { $temp = r_v($fp); print "Expect $offset: $temp\n"; $offset += $local_x; } print "Image data should follow here: reading " . ($local_x * $local_y) . " bytes\n"; my $image; $temp = read($fp,$image,$local_x * $local_y); print "Done reading $temp bytes. Writing.\n"; # write my $out_fname = substr($ARGV[1], 0, rindex($ARGV[1], '.')); $out_fname = $out_fname . "_" . $i . ".bmp"; my $fp2; open ($fp2, ">$out_fname") or die "Couldn't open $out_fname: $!\n"; binmode($fp2); print $fp2 "BM" . pack('V', (14 + 40 + 1024 + ($local_x * $local_y))) . pack('v', 0) . pack('v', 0) . pack('V', (14 + 40 + 1024)); print $fp2 pack('V', 40) . pack('V', $local_x) . pack('V', $local_y) . pack('v', 1) . pack('v', 8) . pack('V', 0) . pack('V', ($local_x * $local_y)) . pack('V', 0) . pack('V', 0) . pack('V', 256) . pack('V', 256); for (my $clr = 0; $clr < 256; $clr ++) { print $fp2 $cmap[$clr][0] . $cmap[$clr][1] . $cmap[$clr][2] . pack('C', 0); } print $fp2 $image; close ($fp2); print "All done writing $out_fname!\n"; } close($fp); |