aboutsummaryrefslogtreecommitdiff
path: root/renderlevels.pl
blob: e96799137ab8f4ce995dc3b253587ab3a4923576 (plain)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/perl -w

# render jumpman junior levels to PNG files, by reading the graphics
# and map data from the ROM image. input is 'jumpman.rom' in the current
# directory, output is level01.png through level12.png in the current dir.

# this code won't win any beauty contests, but it does work.

$verbose = 0; # 0 = quiet, 2 = very chatty

$black_is_white = 0; # 1 = draw all-black shapes in white. only affects
                     # level09.png (the bombs are invisible there).

use Image::Magick;
use bytes;

sub getbyte {
	return ord(substr($rom, $_[0], 1));
}

sub getword {
	return getbyte($_[0]) + 256 * getbyte($_[0] + 1);
}

sub getdelta {
	return $_[0] < 128 ? ($_[0]) : ($_[0] - 256);
}

# placeholder colors. ideally we'd have a full Atari palette, and read
# the color register values from the level desc in the rom.
%palette = (
		0 => [ 0x0000, 0x0000, 0x0000 ],  # background
		1 => [ 0x0000, 0x0000, 0x8000 ],  # girders, up-ropes
		2 => [ 0x0000, 0x8000, 0x0000 ],  # ladders, down-ropes
		3 => [ 0x8000, 0x0000, 0x8000 ],  # bombs
);

sub draw {
	my ($img, $shape, $dx, $dy, $xpos, $ypos, $copies) = @_;
	warn sprintf "drawing shape %04x at $xpos, $ypos, $copies copies, delta ($dx, $dy)\n", $shape if $verbose > 1;
	my $white = 0;
	if($black_is_white && ($shape == 0x9c89 || $shape == 0x9ceb || $shape == 0x9c49)) {
		warn "drawing all-black shape in white\n";
		$white = 1;
		#$shape = 0x9c89; # see the other black shapes in level09.png
		#$shape = 0x9ceb;
	}
	while($copies--) {
		my $width;
		my $addr = $shape;
		while(($width = getbyte($addr++)) != 0xff) {
			my $xoffs = getdelta(getbyte($addr++));
			my $yoffs = getdelta(getbyte($addr++));
			for(my $p = 0; $p < $width; $p++) {
				my $pixel = getbyte($addr++);
				my $color = $palette{$pixel};
				if($white) {
					$color = [ 0xffff, 0xffff, 0xffff]; # make invisible stuff show up white
				}
				$img->SetPixel(x => $xpos + $xoffs + $p, y => $ypos + $yoffs, color => $color);
			}
		}
		$xpos += $dx;
		$ypos += $dy;
	}
}

open ROM, "<jumpmanjr.rom" or die $!;
if((read ROM, $rom, 0x4000, 0x8000) != 0x4000) {
	die "couldn't read ROM\n";
}

for my $level (1..12) {
	my $img = Image::Magick->new;
	$img->Set(size => '160x100');
	$img->ReadImage('canvas:black');

	my $desc = 0xa000 + 0x40 * ($level - 1) + 22;
	my $mapaddr = getword($desc);

	if($level == 9) {
		# show real map for blackout, not the blank one.
		# the bombs are still invisible though.
		$mapaddr = 0xb000;
	}

	warn sprintf("level $level, map pointer at \$%04x, points to \$%04x\n", $desc, $mapaddr) if $verbose;

	# don't initialize these, we want warnings if some level
	# tries to draw without selecting shape and direction.
	my $shape;
	my $copies;
	my $dx;
	my $dy;
	my $xpos;
	my $ypos;

	while(1) {
		my $opcode = getbyte($mapaddr);
		if($opcode eq 0xff) {
			warn sprintf "  got end opcode (\$ff) at \$%04x\n", $mapaddr if $verbose > 1;
			last;
		}

		my $operand1 = getbyte($mapaddr + 1);
		my $operand2 = getbyte($mapaddr + 2);
		my $opword = getword($mapaddr + 1);
		warn sprintf "%04x:   %02x   %02x %02x\n", $mapaddr, $opcode, $operand1, $operand2 if $verbose > 1;
		$mapaddr += 3;

		if($opcode == 0xfe) {
			$shape = $opword;
			warn sprintf "set shape %04x\n", $shape if $verbose > 1;
		} elsif($opcode == 0xfd) {
			$dx = getdelta($operand1);
			$dy = getdelta($operand2);
			warn "set delta X $dx, delta Y $dy\n" if $verbose > 1;
		} elsif($opcode == 0xfc) {
			$mapaddr = $opword;
			next;
		} else {
			$xpos = $opcode;
			$ypos = $operand1;
			$copies = $operand2;
			draw($img, $shape, $dx, $dy, $xpos, $ypos, $copies);
		}
	}

	my $pngfile = sprintf("level%02d.png", $level);
	open my $out, ">$pngfile";
	$img->Write(file => $out, filename => $pngfile);
	close $out;
	warn "wrote $pngfile\n";
}