aboutsummaryrefslogtreecommitdiff
path: root/renderlevels.pl
blob: 8d0140ca1c55c56ff2892ffc953616665d0ef7e8 (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
#!/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

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;
	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};
				$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";
}