Graphics (16-bit)

Graphics for the Amiga/Atari ST versions of Wicked were by Neil Strudwick.

For the purposes of labelling the Amiga disassembly of Wicked, Maptapper was originally used to find some of the sprites and graphics locations in memory. A savestate(.uss) from WinUAE 4.9.1 was used to dump 512kb of Amiga memory. The graphics use 4 bitplanes, and are in AmigaILBM/AtariST Mode. Later, when the graphics index table was found, a bespoke python program was written to decode and write the more obfuscated graphics.

Addresses in green have been verified in the disassembly.

ImageNameAddressSize
lose1$3978416×16
lose2$3980416×16
lose3$3988416×16
lose4$3990416×16
lose5$3998416×16
lose6$39A0416×16
lose7$39A8416×16
lose8$39B0416×16
win1$39B8416×16
win2$39C0416×16
win3$39C8416×16
win4$39D0416×16
win5$39D8416×16
win6$39E0416×16
win7$39E8416×16
win8$39F0416×16
eyebg1$39F8416×32
eyebg2$3A38416×28
eyebg3$3A70416×24
eyebg4$3A94416×20
iris1$3AB2416×32
iris2$3AD2416×26
iris3$3AEC416×21
pupil1$3B01416×16
pupil2$3B09416×16
pupil3$3B11416×16
eyelids1$3B19416×48
eyelids2$3B75416×48
eyelids3$3BD1416×48
playerup1$3C2D416×31
playerup2$3C4C416×31
playerup3$3C6B416×31
playerdown1$3C8A416×31
playerdown2$3CA9416×31
playerdown3$3CC8416×31
playerleft1$3CE7416×31
playerleft2$3D06416×31
playerleft3$3D25416×31
playerright1$3D44416×31
playerright2$3D63416×31
playerright3$3D82416×31
playerupright1$3DA1416×31
playerupright2$3DC0416×31
playerupright3$3DDF416×31
playerdownright1$3DFE416×31
playerdownright2$3E1D416×31
playerdownright3$3E3C416×31
playerupleft1$3E5B416×31
playerupleft2$3E7A416×31
playerupleft3$3E99416×31
playerdownleft1$3EB8416×31
playerdownleft2$3ED7416×31
playerdownleft3$3EF6416×31
playermultifire1$3F15416×32
playermultifire2$3F35416×32
playermultifire3$3F55416×32
playermultifire4$3F75416×32
playershield1$3F95416×31
playershied2$3FB4416×31
playershield3$3FD3416×31
scrollleftend$3FF2416×16
scrollrightend$4002416×16
scrollbar$4012416×14
powercrystal1$4030416×16
powercrystal2$4038416×16
powercrystal3$4040416×16
powercrystal4$4048416×16
guardian1_1$4050416×47
guardian1_2$4096C16×47
guardian1_3$40DD416×47
guardian0_1$4123C16×46
guardian0_2$4168C16×46
guardian0_3$41ADC16×46
guardian0_4$41F2C16×46
guardian0_5$4237C16×46
guardian0_6$427CC
16×46
guardian0_7$42C1C
16×46
guardian2_1$4306C
16×49
guardian2_2$4337C16×49
guardian2_3$4368C16×49
guardian2_4$4399C16×49
guardian2_5$43CAC16×49
handeye1$43FBC16×9
handeye2$4400416×9
handeye3$4404C16×9
handeye4$4409416×9
handeye5$440DC16×9
servantdeath1$4412416×26
servantdeath2$442C416×26
servantdeath3$4446416×26
servantdeath4$4460416×26
servantdeath5$447A416×26
servantdeath6$4494416×26
worm1$44AE416×14
worm2$44BC416×14
worm3$44CA416×14
worm4$44D8416×14
skull1$44E6416×32
skull2$4506416×32
skull3$4526416×32
skull4$4546416×32
skull5$4566416×32
skull6$4586416×32
dagger1$45A6416×32
dagger2$45C6416×32
planchette$45DFC16×16
teardrop1$45EFC16×13
teardrop2$45FCC16×13
teardrop3$4609C16×13
teardrop4$4616C16×13
teardrop5$4623C16×13
teardrop6$4630C16×16
teardrop7$4640C16×16
teardrop8$4650C16×16
teardrop9$4660C16×16
crystalvanish1$4670C16×13
crystalvanish2$4677416×13
crystalvanish3$467DC16×13
crystalvanish4$4684416×13
crystalvanish5$468AC16×13
crystalvanish6$4691416×13
servant0_1$4697C16×16
servant0_2$469FC16×16
servant0_3$46A7C16×16
servant0_4$46AFC16×16
servant0_5$46B7C16×16
servant0_6$46BFC16×16
servant0_7$46C7C16×16
servant2_1$46CFC16×16
servant2_2$46D7C16×16
servant2_3$46DFC16×16
servant2_4$46E7C16×16
servant1_1$46EFC16×16
servant1_2$46F7C16×16
servant1_3$46FFC16×16
servant1_4$4707C16×16
servant1_5$470FC16×16
servant1_6$4717C16×16
servant1_7$471FC16×16
servant1_8$4727C16×16
servant1_9$472FC16×16
servant1_10$4737C16×16
servant5_1$473FC16×16
servant5_2$4747C16×16
servant5_3$474FC16×16
servant5_4$4757C16×16
servant5_5$475FC16×16
servant4_1$4767C16×15
servant4_2$476F416×15
servant4_3$4776C16×15
servant4_4$477E416×15
servant4_5$4785C16×15
servant6_1$478D416×14
servant6_2$4794416×14
servant6_3$479B416×14
servant6_4$47A2416×14
servant6_5$47A9416×14
servant6_6$47B0416×14
servant3_1$47B7416×16
servant3_2$47BF416×16
servant3_3$47C7416×16
servant3_4$47CF416×16
servant3_5$47D7416×16
servant3_6$47DF416×16
servant3_7$47E7416×16
servant3_8$47EF416×16
servant3_9$47F7416×16
guardian3_1$47FF416×58
guardian3_2$4856416×58
guardian3_3$48AD416×58
guardian3_4$4904416×58
guardian3_5$495B416×58
guardian4_1$49B2416×29
guardian4_2$49EC416×29
guardian4_3$4A26416×29
guardian4_4$4A60416×29
guardian5_1$4A9A416×31
guardian5_2$4AD8416×31
guardian5_3$4B16416×31
guardian5_4$4B54416×31
guardian5_5$4B92416×31
guardian5_6$4BD0416×31
guardian5_7$4C0E416×31
guardian5_8$4C4C416×31
expl2_1$4C8A416×15
expl2_2$4C99416×15
expl2_3$4CA8416×15
expl2_4$4CB7416×15
expl2_5$4CC6416×15
expl2_6$4CD5416×15
expl2_7$4CE4416×15
expl2_8$4CF3416×15
guardian6_1$4D02416×31
guardian6_2$4D41416×31
guardian6_3$4D7E416×31
guardian6_4$4DBC416×31
guardian6_5$4DFA416×31
energypointer$4E38416×9
timerpointer$4E3CC16×9
ServantFire_redwhiteballs1$4E41416×7
ServantFire_redwhiteballs2$4E44416×7
ServantFire_redwhiteballs3$4E47416×7
cross1$4E4A416×7
cross2$4E4E416×7
flyingevilspore1$4E51416×7
flyingevilspore2$4E54C16×7
flyingevilspore3$4E58416×7
minisun1$4E5BC16×8
minisun2$4E5FC16×8
minipowercrystal$4E63C16×8
minimoon$4E67C16×8
goodspore1$4E6BC16×8
goodspore2$4E6FC16×8
goodspore3$4E73C16×8
evilspore1$4E77C16×8
evilspore2$4E7BC16×8
evilspore3$4E7FC16×8
evilportal0$4E83C16×14
evilgrowth0_1$4E8AC16×14
evilgrowth0_2$4E91C16×14
evilgrowth0_3$4E98C16×14
evilgrowth0_4$4E9FC16×14
evilgrowth0_5$4EA6C16×14
evilgrowth0_6$4EADC16×14
evilgrowth0_7$4EB4C16×14
goodportal0$4ED0C16×14
goodgrowth0_1$4ED7C16×14
goodgrowth0_2$4EDEC16×14
goodgrowth0_3$4EE5C16×14
goodgrowth0_4$4EECC16×14
goodgrowth0_5$4EF3C16×14
goodgrowth0_6$4EFAC16×14
goodgrowth0_7$4F01C16×14
shotgrowth0_duplicate$4F1DC16×14
shotgrowth0_1$4F24C16×14
shotgrowth0_2$4F2BC16×14
shotgrowth0_3$4F32C16×14
shotgrowth0_4$4F39C16×14
shotgrowth0_5$4F40C16×14
shotgrowth0_6$4F47C16×14
shotgrowth0_7$4F4EC16×14
evilportal3$4F6AC16×14
evilgrowth3_1$4F71C16×14
evilgrowth3_2$4F78C16×14
evilgrowth3_3$4F7FC16×14
evilgrowth3_4$4F86C16×14
evilgrowth3_5$4F8DC16×14
evilgrowth3_6$4F94C16×14
evilgrowth3_7$4F9BC16×14
goodportal3$4FB7C16×14
goodgrowth3_1$4FBEC16×14
goodgrowth3_2$4FC5C16×14
goodgrowth3_3$4FCCC16×14
goodgrowth3_4$4FD3C16×14
goodgrowth3_5$4FDAC16×14
goodgrowth3_6$4FE1C16×14
goodgrowth3_7$4FE8C16×14
unknown1$5004C16×14
shotgrowth3_1$500BC16×14
shotgrowth3_2$5012C16×14
shotgrowth3_3$5019C16×14
shotgrowth3_4$5020C16×14
shotgrowth3_5$5027C16×14
shotgrowth3_6$502EC16×14
shotgrowth3_7$5035C16×14
evilportal2$5051C16×14
evilgrowth2_1$5058C16×14
evilgrowth2_2$505FC16×14
evilgrowth2_3$5066C16×14
evilgrowth2_4$506DC16×14
evilgrowth2_5$5074C16×14
evilgrowth2_6$507BC16×14
evilgrowth2_7$5082C16×14
goodportal2$509EC16×14
goodgrowth2_1$50A5C16×14
goodgrowth2_2$50ACC16×14
goodgrowth2_3$50B3C16×14
goodgrowth2_4$50BAC16×14
goodgrowth2_5$50C1C16×14
goodgrowth2_6$50C8C16×14
goodgrowth2_7$50CFC16×14
unknown2$50EBC16×14
shotgrowth2_1$50F2C16×14
shotgrowth2_2$50F9C16×14
shotgrowth2_3$5100C16×14
shotgrowth2_4$5107C16×14
shotgrowth2_5$510EC16×14
shotgrowth2_6$5115C16×14
shotgrowth2_7$511CC16×14
evilportal1$5138C16×14
evilgrowth1_1$513FC16×14
evilgrowth1_2$5146C16×14
evilgrowth1_3$514DC16×14
evilgrowth1_4$5154C16×14
evilgrowth1_5$515BC16×14
evilgrowth1_6$5162C16×14
evilgrowth1_7$5169C16×14
goodportal1$5185C16×14
goodgrowth1_1$518CC16×14
goodgrowth1_2$5193C16×14
goodgrowth1_3$519AC16×14
goodgrowth1_4$51A1C16×14
goodgrowth1_5$51A8C16×14
goodgrowth1_6$51AFC16×14
goodgrowth1_7$51B6C16×14
(duplicate)$51D2C16×14
shotgrowth1_1$51D9C16×14
shotgrowth1_2$51E0C16×14
shotgrowth1_3$51E7C16×14
shotgrowth1_4$51EEC16×14
shotgrowth1_5$51F5C16×14
shotgrowth1_6$51FCC16×14
shotgrowth1_7$5203C16×14
lifestar1$5220016×14
lifestar2$5226816×14
Eyelids bottom – Index 87$522DC24×16
indicator1 – Index 88$5245216×14
indicator2 – Index 89$524c216×14
indicator3 – Index 90$5253216×14
indicator4 – Index 91$525A216×14
indicator5 – Index 92$5261216×14
indicator6 – Index 93$5268216×14
StarMap Graphics – Compass – Index 1$526f232×31
StarMap tile background 0 – Index 2$528db32×32
StarMap Graphics – Galaxy – Index 3$529f132×29
StarMap Graphics – Comet – Index 4$52ad632×25
StarMap Graphics – Star Pattern – Index 5$52b7932×27
StarMap tile background 1 – Index 75$52c3432×32
StarMap tile background 2 – Index 76$52d4832×32
StarMap Graphics – Cherub facing left – Index 77$52e5d32×32
StarMap Graphics – Cherub facing right – Index 78$5304c32×32
StarMap Graphics – Sexton – Index 79$531d232×32
StarMap Graphics – Star pattern – Index 80$5333732×32
StarMap Graphics – Lizard up – Index 81$533c032×30
StarMap Graphics – Lizard down – Index 82$5357232×30
StarMap Graphics – SeaMonster facing right – Index 83$5372332×31
StarMap Graphics – SeaMonster facing left – Index 84$5390c32×31
Bird/Snake Energy/Time Meter – Index 8$53ae816×95
Eye of Infinity Stone Circle – Index 0$53d98224×199
Wicked Sarsen Stones – Index 7$578fa288×68
Sunset Stone Shadow – Index 6$59b46320×2
Large Player Circle 1$5a54664×64
Large Player Circle 2$5ad4664×64
Large Player Flames 1$5b54664×64
Large Player Flames 2$5bd4664×64
Large Player Flames 3$5c54664×64
Vitruvius Man$5cd4664×64
Beast 3$5d54664×64
Beast 4$5dd4664×64
Beast 5$5e54664×64
Beast 6$5ed4664×64
Beast 7$5f54664×64
Beast 2$5fd4664×64
Beast 1$6054664×64
Sun Winking – Index 56$60d4632×26
Sun1 (title screen?) – Index 86$60ee664×64
Sun 2$616e664×64
Sun 3$61ee664×64
Sun 4$626e664×64
Sun1 (in Constellation Won?) – Index 55$62ee664×64
Tarot 0$636e632×64
Tarot 1$63ae632×64
Tarot 2$63ee632×64
Tarot 3$642e632×64
Tarot 4$646e632×64
Tarot 5$64ae632×64
Tarot 6$64ee632×64
Tarot 7$652e632×64
Glyph 0 – Index 10$656e616×15
Glyph 1 – Index 11$6570416×15
Glyph 2 – Index 12$6572216×15
Glyph 3 – Index 13$6574016×15
Glyph 4 – Index 14$6575e16×15
Glyph 5 – Index 15$6577c16×15
Glyph 6 – Index 16$6579a16×15
Glyph 7 – Index 17$657b816×15
Glyph 8 – Index 18$657d616×15
Glyph 9 – Index 19$657f416×15
Small Glyph 0 – Index 57$658128×8
Small Glyph 1 – Index 58$658328×8
Small Glyph 2 – Index 59$658528×8
Small Glyph 3 – Index 60$658728×8
Small Glyph 4 – Index 61$658928×8
Small Glyph 5 – Index 62$658b28×8
Small Glyph 6 – Index 63$658d28×8
Small Glyph 7 – Index 64$658f28×8
Small Glyph 8 – Index 65$659128×8
Small Glyph 9 – Index 66$659128×8
Glyph A – Index 20$6595216×16
Glyph B – Index 21$659d216×16
Glyph C – Index 22$65a5216×16
Glyph D – Index 23$65ad216×16
Glyph E – Index 24$65b5216×16
Glyph F – Index 25$65bd216×16
Glyph G – Index 26$65c5216×16
Glyph H – Index 27$65cd216×16
Glyph I – Index 28$65d5216×16
Glyph J – Index 29$65dd216×16
Glyph K – Index 30$65e5216×16
Glyph L – Index 31$65ed216×16
Glyph M – Index 32$65f5216×16
Glyph N – Index 33$65fd216×16
Glyph O – Index 34$6605216×16
Glyph P – Index 35$660d216×16
Glyph Q – Index 36$6615216×16
Glyph R – Index 37$661d216×16
Glyph S – Index 38$6625216×16
Glyph T – Index 39$662d216×16
Glyph U – Index 40$6635216×16
Glyph V – Index 41$663d216×16
Glyph W – Index 42$6645216×16
Glyph X – Index 43$664d216×16
Glyph Y – Index 44$6655216×16
Glyph Z – Index 45$665d216×16
Glyph copyright – Index 85$6665216×16
Glyph dot – Index 46$666d216×16
Glyph dash – Index 47$6675216×16
Glyph colon – Index 48$667d216×16
Hi-Score Ouija Board (top) – Index 49$6685288×67
Hi-Score Ouija Board (left) – Index 50$684d732×63
Hi-Score Ouija Board (right) – Index 51$68e7e32×63
Hi-Score Ouija Board (bottom) – Index 52$6a05988×66
Hi-Score scroll bar – Index 53$6a05948×15
Hi-Score scroll top (shadow) – Index 94$6a34348×8
Hi-Score scroll background – Index 54$6a41248×20
Hi-Score scroll bottom – Index 95$6a5d748×7
Grave Headstone Anim 1 – Index 67$6a69816×6
Grave Headstone Anim 2 – Index 68$6a74016×18
Grave Headstone Anim 3 – Index 69$6a90816×32
Grave Headstone Anim 4 – Index 70$6ac2016×41
Grave Headstone Anim 5 – Index 71$6b01016×48
Grave Grass – Index 72$6b4a816×18
Grave Gravestone top – Index 73$6b67040×20
Grave Gravestone bottom – Index 74$6b9b764×53

Colour palette 1 (in-game)Colour palette 2 (devil)
Total chip memory: $80000 (512kb)
Chip memory offset in savestate: $8AC
Copperlist address in chip memory: $37750
Copperlist offset in savestate: $37FFC
Copperlist 2 address in chip memory: $D528
Copperlist 2 offset in savestate: $DDD4
Total chip memory: $80000 (512kb)
Chip memory offset in savestate: $8AC
Copperlist address in chip memory: $800
Copperlist offset in savestate: $10AC
Copperlist 2 address in chip memory: $D4E0
Copperlist 2 offset in savestate: $DD8C
Chip Addr: Copper instructions ; Comments
$37750: 00E0 0001 00E2 0000 ; BPL1PT = 0x00010000
$37758: 00E4 0001 00E6 1F40 ; BPL2PT = 0x00011F40
$37760: 00E8 0001 00EA 3E80 ; BPL3PT = 0x00013E80
$37768: 00EC 0001 00EE 5DC0 ; BPL4PT = 0x00015DC0
$37770: ffff fffe ; End of copperlist
$37770: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE
Chip Addr: Copper instructions ; Comments
$00000800: 00E0 0001 00E2 0000 ; BPL1PT = 0x00010000
$00000808: 00E4 0001 00E6 1F40 ; BPL2PT = 0x00011F40
$00000810: 00E8 0001 00EA 3E80 ; BPL3PT = 0x00013E80
$00000818: 00EC 0001 00EE 5DC0 ; BPL4PT = 0x00015DC0
$00000820: 1801 FF00 ; Wait for vpos >= 0x18 and hpos >= 0x00
$00000824: 0180 0000 ; COLOR00 = 0x0000
$00000828: ffff fffe ; End of copperlist
$00000828: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE

00: Hex: #000000 RGB (0, 0, 0)
01: Hex: #777777 RGB (119, 119, 119)
02: Hex: #779977 RGB (119, 153, 119)
03: Hex: #77BB99 RGB (119, 187, 153)
04: Hex: #FF0000 RGB (255, 0, 0)
05: Hex: #BB0000 RGB (187, 0, 0)
06: Hex: #FFDD00 RGB (255, 221, 0)
07: Hex: #DD9900 RGB (221, 153, 0)
08: Hex: #FFBB00 RGB (255, 187, 0)
09: Hex: #BB77FF RGB (187, 119, 255)
10: Hex: #BBBBBB RGB (187, 187, 187)
11: Hex: #55FFBB RGB (85, 255, 187)
12: Hex: #00BB00 RGB (0, 187, 0)
13: Hex: #770000 RGB (119, 0, 0)
14: Hex: #0000FF RGB (0, 0, 255)
15: Hex: #FFFFFF RGB (255, 255, 255)

00: Hex: #000000 RGB (0, 0, 0)
01: Hex: #337777 RGB (51, 119, 119)
02: Hex: #779977 RGB (119, 153, 119)
03: Hex: #77BB99 RGB (119, 187, 153)
04: Hex: #FF0000 RGB (255, 0, 0)
05: Hex: #BB0000 RGB (187, 0, 0)
06: Hex: #FFDD00 RGB (255, 221, 0)
07: Hex: #DD9900 RGB (221, 153, 0)
08: Hex: #FFBB00 RGB (255, 187, 0)
09: Hex: #000000 RGB (0, 0, 0)
10: Hex: #000055 RGB (0, 0, 85)
11: Hex: #555577 RGB (85, 85, 119)
12: Hex: #DD0000 RGB (221, 0, 0)
13: Hex: #BB0000 RGB (187, 0, 0)
14: Hex: #FF0000 RGB (255, 0, 0)
15: Hex: #BB0000 RGB (187, 0, 0)
Colour palette 3 (eye)
Total chip memory: $80000 (512kb)
Chip memory offset in savestate: $D2C
Copperlist address in chip memory: $37774
Copperlist offset in savestate: $384A0
Copperlist 2 address in chip memory: $D590
Copperlist 2 offset in savestate: $E2BC
Chip Addr: Copper instructions ; Comments
$00037774: 00E0 0001 00E2 8000 ; BPL1PT = 0x00018000
$0003777C: 00E4 0001 00E6 9F40 ; BPL2PT = 0x00019F40
$00037784: 00E8 0001 00EA BE80 ; BPL3PT = 0x0001BE80
$0003778C: 00EC 0001 00EE DDC0 ; BPL4PT = 0x0001DDC0
$00037794: ffff fffe ; End of copperlist
$00037794: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE

00: Hex: #000000 RGB (0, 0, 0)
01: Hex: #DDDDDD RGB (221, 221, 221)
02: Hex: #99DD00 RGB (143, 221, 0)
03: Hex: #55BB55 RGB (85, 187, 85)
04: Hex: #007700 RGB (0, 119, 0)
05: Hex: #333399 RGB (51, 51, 153)
06: Hex: #BB0000 RGB (187, 0, 0)
07: Hex: #DD0000 RGB (221, 0, 0)
08: Hex: #990000 RGB (153, 0, 0)
09: Hex: #550000 RGB (85, 0, 0)
10: Hex: #773300 RGB (119, 51, 0)
11: Hex: #995500 RGB (153, 85, 0)
12: Hex: #BB7700 RGB (187, 119, 0)
13: Hex: #DD9900 RGB (221, 153, 0)
14: Hex: #FFBB00 RGB (255, 187, 0)
15: Hex: #FFDD77 RGB (225, 221, 119)

00: Hex: #000000 RGB (0, 0, 0)
01: Hex: #337777 RGB (51, 119, 119)
02: Hex: #779977 RGB (119, 153, 119)
03: Hex: #77BB99 RGB (119, 187, 153)
04: Hex: #FF0000 RGB (255, 0, 0)
05: Hex: #BB0000 RGB (187, 0, 0)
06: Hex: #FFDD00 RGB (255, 221, 0)
07: Hex: #DD9900 RGB (221, 153, 0)
08: Hex: #FFBB00 RGB (255, 187, 0)
09: Hex: #00000 RGB (0, 0, 0)
10: Hex: #000055 RGB (0, 0, 85)
11: Hex: #555577 RGB (85, 85, 119)
12: Hex: #999999 RGB (153, 153, 153)
13: Hex: #7799BB RGB (119, 153, 187)
14: Hex: #BBBBFF RGB (187, 187, 255)
15: Hex: #557799 RGB (85, 119, 153)

The functions SUB_RenderText_2E47E and SUB_DrawToScreen_2E4F0 use a lookup table at $3402A, which is decoded as:

IndexGraphicWidthHeightModePointerNotes
0Eye of Infinity Stone Circle$d$c6$3$53d98Run-length encoding (via SUB_2E6CA)
1StarMap Graphics – Star Wheel$1$1e$3$526f2Run-length encoding (via SUB_2E6CA)
2StarMap Graphics – TileBackground$1$1f$3$528dbRun-length encoding (via SUB_2E6CA)
3StarMap Graphics – Galaxy$1$1c$3$529f1Run-length encoding (via SUB_2E6CA)
4StarMap Graphics – Comet$1$18$3$52ad6Run-length encoding (via SUB_2E6CA)
5StarMap Graphics – Star Pattern$1$1a$3$52b79Run-length encoding (via SUB_2E6CA)
6$13$1$0$59b46Default glyph render
7Wicked Sarsen Stones$11$43$3$578faRun-length encoding (via SUB_2E6CA); likely sprite/large overlay
8Bird/Snake Energy/Time Meter$0$5e$3$53ae8Run-length encoding (via SUB_2E6CA)
9(padding)$0$0$0$00000(empty/ invalid entry)
10Glyph 0$0$e$ffff$656e6Direct blit
11Glyph 1$0$e$ffff$65704Direct blit
12Glyph 2$0$e$ffff$65722Direct blit
13Glyph 3$0$e$ffff$65740Direct blit
14Glyph 4$0$e$ffff$6575eDirect blit
15Glyph 5$0$e$ffff$6577cDirect blit
16Glyph 6$0$e$ffff$6579aDirect blit
17Glyph 7$0$e$ffff$657b8Direct blit
18Glyph 8$0$e$ffff$657d6Direct blit
19Glyph 9$0$e$ffff$657f4Direct blit
20Glyph A$0$f$0$65952Default glyph render
21Glyph B$0$f$0$659d2Default glyph render
22Glyph C$0$f$0$65a52Default glyph render
23Glyph D$0$f$0$65ad2Default glyph render
24Glyph E$0$f$0$65b52Default glyph render
25Glyph F$0$f$0$65bd2Default glyph render
26Glyph G$0$f$0$65c52Default glyph render
27Glyph H$0$f$0$65cd2Default glyph render
28Glyph I$0$f$0$65d52Default glyph render
29Glyph J$0$f$0$65dd2Default glyph render
30Glyph K$0$f$0$65e52Default glyph render
31Glyph L$0$f$0$65ed2Default glyph render
32Glyph M$0$f$0$65f52Default glyph render
33Glyph N$0$f$0$65fd2Default glyph render
34Glyph O$0$f$0$66052Default glyph render
35Glyph P$0$f$0$660d2Default glyph render
36Glyph Q$0$f$0$66152Default glyph render
37Glyph R$0$f$0$661d2Default glyph render
38Glyph S$0$f$0$66252Default glyph render
39Glyph T$0$f$0$662d2Default glyph render
40Glyph U$0$f$0$66352Default glyph render
41Glyph V$0$f$0$663d2Default glyph render
42Glyph W$0$f$0$66452Default glyph render
43Glyph X$0$f$0$664d2Default glyph render
44Glyph Y$0$f$0$66552Default glyph render
45Glyph Z$0$f$0$665d2Default glyph render
46Glyph dot$0$f$0$666d2Default glyph render
47Glyph dash$0$f$0$66752Default glyph render
48Glyph colon$0$f$0$667d2Default glyph render
49Hi-Score Ouija Board (top)$b$43$3$66852Run-length encoding (via SUB_2E6CA); likely sprite/large overlay
50Hi-Score Ouija Board (left)$4$3f$3$67b4fRun-length encoding (via SUB_2E6CA)
51Hi-Score Ouija Board (right)$4$3f$3$684d7Run-length encoding (via SUB_2E6CA)
52Hi-Score Ouija Board (bottom)$b$42$3$68e7eRun-length encoding (via SUB_2E6CA)
53Hi-Score Scroll bar$6$f$3$6a059Run-length encoding (via SUB_2E6CA)
54Hi-Score Scroll background$6$14$3$6a412Run-length encoding (via SUB_2E6CA)
55Sun1 (in Constellation Won?)$3$3f$0$62ee6Default glyph render
56Sun Wink$1$19$0$60d46Default glyph render
57Glyph 0 (small)$0$7$1$65812Handled in BitplaneMaskedWrite (mode 1 branch)
58Glyph 1 (small)$0$7$1$65832Handled in BitplaneMaskedWrite (mode 1 branch)
59Glyph 2 (small)$0$7$1$65852Handled in BitplaneMaskedWrite (mode 1 branch)
60Glyph 3 (small)$0$7$1$65872Handled in BitplaneMaskedWrite (mode 1 branch)
61Glyph 4 (small)$0$7$1$65892Handled in BitplaneMaskedWrite (mode 1 branch)
62Glyph 5 (small)$0$7$1$658b2Handled in BitplaneMaskedWrite (mode 1 branch)
63Glyph 6 (small)$0$7$1$658d2Handled in BitplaneMaskedWrite (mode 1 branch)
64Glyph 7 (small)$0$7$1$658f2Handled in BitplaneMaskedWrite (mode 1 branch)
65Glyph 8 (small)$0$7$1$65912Handled in BitplaneMaskedWrite (mode 1 branch)
66Glyph 9 (small)$0$7$1$65932Handled in BitplaneMaskedWrite (mode 1 branch)
67Grave Headstone Anim 1$2$6$0$6a698Default glyph render
68Grave Headstone Anim 2$2$12$0$6a740Default glyph render
69Grave Headstone Anim 3$2$20$0$6a908Default glyph render
70Grave Headstone Anim 4$2$29$0$6ac20Default glyph render
71Grave Headstone Anim 5$2$30$0$6b010Default glyph render
72Grave Grass$2$12$0$6b4a8Default glyph render
73Grave Gravestone top$5$14$3$6b670Run-length encoding (via SUB_2E6CA)
74Grave Gravestone bottom$8$35$3$6b9b7Run-length encoding (via SUB_2E6CA); likely sprite/large overlay
75StarMap tile background 1$1$1f$5$52c34Run-length encoding (via SUB_2E6CA)
76StarMap tile background 2$1$1f$5$52d48Run-length encoding (via SUB_2E6CA)
77StarMap Graphics – Cherub facing left$1$1f$3$52e5dRun-length encoding (via SUB_2E6CA)
78StarMap Graphics – Cherub facing right$1$1f$3$5304cRun-length encoding (via SUB_2E6CA)
79StarMap Graphics – Sexton$1$1f$3$531d2Run-length encoding (via SUB_2E6CA)
80StarMap Graphics – Star pattern$1$1f$3$53337Run-length encoding (via SUB_2E6CA)
81StarMap Graphics – Lizard up$1$1d$3$533c0Run-length encoding (via SUB_2E6CA)
82StarMap Graphics – Lizard down$1$1d$3$53572Run-length encoding (via SUB_2E6CA)
83StarMap Graphics – Seamonster facing right$1$1e$3$53723Run-length encoding (via SUB_2E6CA)
84StarMap Graphics – Seamonster facing left$1$1e$3$5390cRun-length encoding (via SUB_2E6CA)
85Glyph Copyright Symbol$0$f$0$66652Default glyph render
86Sun1$3$3f$0$60ee6Default glyph render
87Bottom of Eye of Infinity bottom eyelid$3$f$3$522dcRun-length encoding (via SUB_2E6CA)
88$0$d$6$52452Masked rendering (via PrepareMaskedRendering_2E594)
89Eye Arrow Marker?$0$d$6$524c2Masked rendering (via PrepareMaskedRendering_2E594)
90Eye Arrow Marker?$0$d$6$52532Masked rendering (via PrepareMaskedRendering_2E594)
91Eye Arrow Marker?$0$d$6$525a2Masked rendering (via PrepareMaskedRendering_2E594)
92Eye Arrow Marker?$0$d$6$52612Masked rendering (via PrepareMaskedRendering_2E594)
93Eye Arrow Marker?$0$d$6$52682Masked rendering (via PrepareMaskedRendering_2E594)
94Hi Score scroll top (shadow)$6$8$3$6a343Run-length encoding (via SUB_2E6CA)
95Hi Score scroll bottom$6$7$3$6a5d7Run-length encoding (via SUB_2E6CA)

Modes 3 and 5 involve RLE decoding, then plane assembly for interleaved [p0,p1,p2,p3] words.


Rendering the data

All graphics are stored for 4 bitplanes (16 palette indices). Per table row:

Width: Number of 16-pixel cells for 16-bit formats (modes 0, 2, 3, 5, FFFF), or number of 8-pixel cells for byte-masked mode (mode 1). If the field is 0, treat it as 1 cell.

Height: Number of rows. Use it as-is (do not add 1).

Mode: Selects how to decode the bytes into 4 bitplanes.

Note on Palettes: Wicked uses multiple palettes depending on the game context (e.g., UI, sprites, backgrounds). The rendered colors may vary if the wrong palette is applied to an index.

Mode 0000, Mode 0002, and Mode 0006

Format: 4 planes, 16 pixels wide per cell.

Per cell per row: 8 bytes = 4 words in this order: P0, P1, P2, P3.

Row layout: Left-to-right cells, then next row.

Pixel color index at x: (P0>>bit)&1 | (((P1>>bit)&1)<<1) | (((P2>>bit)&1)<<2) | (((P3>>bit)&1)<<3) with bit 15 at the leftmost pixel.

Resulting width: 16 * Width (or 16 if Width is 0).

Notes:

  • Mode 0002 uses a straight copy path in the code, but the data layout is the same as mode 0000 for offline rendering.
  • Mode 0006 uses a masked write, but the source words are still P0, P1, P2, P3 per cell for offline rendering.
  • Example: Index 67 (mode 0, Width=0001, Height=0006) renders as a 32×7 pixel sprite.

Mode FFFF (Direct Blit)

Format: 1-bit bitmap (single plane).

Per row per cell: 1 word. Set plane P0 to the word, P1..P3 zero.

Resulting width: 16 * Width (or 16 if Width is 0).

Notes: Used for large glyphs (e.g., indices 10–19 for digits 0–9, 16×15 pixels).

Mode 0001 (Byte-Masked)

Format: 4 planes, 8 pixels wide per cell.

Per cell per row: 4 bytes in this order: B0, B1, B2, B3 mapping to planes P0..P3.

Pixel color index at x: (B0>>bit)&1 | (((B1>>bit)&1)<<1) | (((B2>>bit)&1)<<2) | (((B3>>bit)&1)<<3) with bit 7 at the leftmost pixel.

Resulting width: 8 * Width (or 8 if Width is 0).

Notes: Used for small 8×8 glyphs (e.g., indices 57–66 for digits 0–9).

Mode 0003 and Mode 0005 (Stream with RLE and Reformat)

The data stream begins with two control bytes: C_hi and C2.

Decode bytes until you hit the special stop marker:

  • If (byte & 0xF0) != C_hi: Treat the byte as a literal and append it.
  • Else read a count L:
    • If L == 0: The original byte is literal, append it.
    • Else let N = byte & 0x0F:
      • N == 0: Append L zero bytes.
      • N == 1: Stop the RLE pass and go to reformat step.
      • N > 1: Append N copies of byte L.

Reformat step (offline simplification that matches the draw code outcome):

  • Let U = Width * Height (using rules at top; Width of 0 means 1).
  • Interpret the decoded byte buffer as 4 contiguous blocks of U words (big-endian), i.e., P0[0..U-1], P1[0..U-1], P2[0..U-1], P3[0..U-1]. Pad with zeros if short.
  • For cell i (row-major): Use words P0[i], P1[i], P2[i], P3[i] to render 16 pixels as in Mode 0000.

Note: The in-game routine has an extra mirroring copy for some effects; the block interleave above produces the correct final pixels for offline rendering. Example: Index 75 (mode 5, Width=0001, Height=001f) renders a 32×31 pixel sprite.

Text Renderer Notes (SUB_RenderText_2E47E)

Each printed character advances by 8 in X.

Newline is a literal ‘#’: X resets to start of line and adds 0x0AA0 to Y.

Special remaps: ‘.’ to 0x002E, ‘-‘ to 0x002F, ‘:’ to 0x0030.

Assumptions and Cross-Checks

Mode 0003 and 0005: Use the stream RLE followed by the 4-block interleave described above.

Width, Height, Mode: Taken from per-index comments in the data file or fallback metadata. For glyph blocks (e.g., indices 20–48), Mode 0000 and Height=000e produce correct 15-row glyphs.

Mode FFFF: Entries are 1-bit words per row, decoding as expected for simple shapes (e.g., indices 10–19, large digits 0–9).

Mode 0001: Decodes to 8-pixel-wide cells using 4 bytes per cell (e.g., indices 57–66, small digits 0–9).

Mode 0002 and 0006: Use the same per-cell P0..P3 word layout for offline decode.


Python Wicked Graphics Export Tool

This python code was used to decode the graphics, and a text file called graphicsdata.txt in the same directory as this code of the disassembled area of memory containing the graphics in the graphics table at $3402A with a comment after each label to explain the width, height and mode for that entry in the table. For example:

GraphicsIndex_87:
; Graphics Index 87, Width=0003, Height=000f, Mode=0003, Notes=Special case (via SUB_2E6CA)
DC.L $a0010263,$80000001,$ffc00130,$7c00001f ;522dc
(etc.)

It also requires Pillow (pip install Pillow) to be installed so that you can save the graphics out as .png files.
Run the program with

python wicked_renderer.py
or
python wicked_renderer.py –scale 2

(for outputting graphics at 2x size)

import struct
from PIL import Image
import re
import argparse

# Wicked palette (default)
WICKED_PALETTE = [
    (0, 0, 0),      # 0: #000000
    (51, 119, 119), # 1: #337777
    (119, 153, 119),# 2: #779977
    (119, 187, 153),# 3: #77BB99
    (255, 0, 0),    # 4: #FF0000
    (187, 0, 0),    # 5: #BB0000
    (255, 221, 0),  # 6: #FFDD00
    (221, 153, 0),  # 7: #DD9900
    (255, 187, 0),  # 8: #FFBB00
    (0, 0, 0),      # 9: #000000
    (0, 0, 85),     # 10: #000055
    (85, 85, 119),  # 11: #555577
    (153, 153, 153),# 12: #999999
    (119, 153, 187),# 13: #7799BB
    (187, 187, 255),# 14: #BBBBFF
    (85, 119, 153)  # 15: #557799
]

# UI palette for cursors (indices 88–93, if needed)
WICKED_PALETTE_UI = [
    (0, 0, 0),      # 0: Black #000000
    (255, 255, 255),# 1: White #FFFFFF
    (255, 255, 255),# 2: White #FFFFFF
    (255, 255, 255),# 3: White #FFFFFF
    (255, 255, 255),# 4: White #FFFFFF
    (0, 0, 0),      # 5: Black #000000
    (0, 0, 0),      # 6: Black #000000
    (0, 0, 0),      # 7: Black #000000
    (0, 0, 0),      # 8: Black #000000
    (0, 0, 0),      # 9: Black #000000
    (0, 0, 0),      # 10: Black #000055
    (0, 0, 0),      # 11: Black #555577
    (0, 0, 0),      # 12: Black #999999
    (0, 0, 0),      # 13: Black #7799BB
    (0, 0, 0),      # 14: Black #BBBBFF
    (0, 0, 0)       # 15: Black #557799
]

def parse_assembler_data(data_text):
    """Parse Amiga Assembler data (DC.L, DC.W, DC.B, DS.L) into byte arrays."""
    indices = {}
    current_index = None
    current_data = bytearray()
    width, height, mode = None, None, None
    lines = data_text.splitlines()
    line_idx = 0
    
    while line_idx < len(lines):
        line = lines[line_idx]
        orig_line = line
        line = line.split(';', 1)[0].strip()
        if not line:
            line_idx += 1
            continue
            
        # Check for any label ending with a colon, excluding assembler directives
        if line.endswith(':') and not any(line.startswith(d) for d in ['DC.L', 'DC.W', 'DC.B', 'DS.L']):
            if current_index is not None:
                indices[current_index] = {
                    'data': bytes(current_data),
                    'width_minus_1': width,
                    'height_minus_1': height,
                    'mode': mode
                }
            current_index = line[:-1]  # Remove the colon
            current_data = bytearray()
            # Look for metadata in the next line
            if line_idx + 1 < len(lines):
                next_line = lines[line_idx + 1]
                match = re.search(r'(Width|Depth|W)=([0-9a-fA-F]+)[\s,;:]*(Height|Depth|H)=([0-9a-fA-F]+)[\s,;:]*(Mode|M)=([0-9a-fA-F]+)', next_line, re.IGNORECASE)
                if match:
                    width = int(match.group(2), 16)
                    height = int(match.group(4), 16)
                    mode = int(match.group(6), 16)
                    if mode == 0xFFFF or mode == -1:
                        mode = -1
                    print(f"Parsed metadata for {current_index}: Width={width:04x}, Height={height:04x}, Mode={mode:04x}")
                else:
                    print(f"Warning: Could not parse metadata for {current_index}: {orig_line}")
                    print(f"Next line: {next_line}")
                    width, height, mode = None, None, None
            else:
                print(f"Warning: Could not parse metadata for {current_index}: {orig_line}")
                print("No next line available")
                width, height, mode = None, None, None
        elif 'DC.L' in line:
            values_part = line.split('DC.L')[1].strip()
            if values_part:
                values = [v.strip() for v in values_part.split(',') if v.strip()]
                for v in values:
                    try:
                        val = int(v.replace('$', '0x'), 16)
                        if mode == 6:
                            # Take first 4 bytes for mode 6, skip duplicates
                            current_data.extend(struct.pack('>I', val)[:4])
                        else:
                            current_data.extend(struct.pack('>I', val))
                    except ValueError as e:
                        print(f"Warning: Skipping invalid DC.L value '{v}' in line: {line}")
        elif 'DC.W' in line:
            values_part = line.split('DC.W')[1].strip()
            if values_part:
                values = [v.strip() for v in values_part.split(',') if v.strip()]
                for v in values:
                    try:
                        val = int(v.replace('$', '0x'), 16)
                        current_data.extend(struct.pack('>H', val))
                    except ValueError as e:
                        print(f"Warning: Skipping invalid DC.W value '{v}' in line: {line}")
        elif 'DC.B' in line:
            values_part = line.split('DC.B')[1].strip()
            if values_part:
                values = [v.strip() for v in values_part.split(',') if v.strip()]
                for v in values:
                    try:
                        val = int(v.replace('$', '0x'), 16)
                        current_data.append(val)
                    except ValueError as e:
                        print(f"Warning: Skipping invalid DC.B value '{v}' in line: {line}")
        elif 'DS.L' in line and mode != 6:  # Skip DS.L for mode 6
            values_part = line.split('DS.L')[1].strip()
            if values_part:
                try:
                    count = int(values_part.replace('$', '0x'), 16)
                    current_data.extend([0] * count * 4)
                except ValueError as e:
                    print(f"Warning: Skipping invalid DS.L count '{values_part}' in line: {line}")
        line_idx += 1
    
    if current_index is not None:
        indices[current_index] = {
            'data': bytes(current_data),
            'width_minus_1': width,
            'height_minus_1': height,
            'mode': mode
        }
    
    return indices

def decode_wicked_bitmap(mode, width_minus_1, height_minus_1, data):
    """Decode bitmap for any mode."""
    cols = width_minus_1 + 1
    rows = height_minus_1 + 1
    U = cols * rows
    cell_size_px = 16 if mode in [0, 2, 3, 5, -1] else 8
    expected_size = 8 * U if mode in [0, 2, 3, 5] else 4 * U
    
    if mode in [3, 5]:
        by_plane = bytearray()
        pos = 0
        if len(data) < 2:
            print(f"Warning: Data too short for mode {mode}: {len(data)} bytes")
            return bytearray(expected_size * 2)
        ctrl_hi = data[0]
        ctrl2 = data[1]
        pos += 2
        
        while pos < len(data):
            b = data[pos]
            pos += 1
            if (b & 0xF0) == (ctrl_hi & 0xFF):
                c = data[pos] if pos < len(data) else 0
                pos += 1
                n = b & 0x0F
                if c == 0:
                    by_plane.append(b)
                elif n == 0:
                    by_plane.extend([0] * c)
                elif n == 1:
                    break
                else:
                    by_plane.extend([c] * n)
            else:
                by_plane.append(b)
        
        if len(by_plane) != expected_size * 2:
            print(f"Warning: Decoded size {len(by_plane)} bytes, expected {expected_size * 2}")
            if len(by_plane) < expected_size * 2:
                by_plane.extend([0] * (expected_size * 2 - len(by_plane)))
            else:
                by_plane = by_plane[:expected_size * 2]
        
        interleaved = bytearray(len(by_plane))
        if ctrl2 != 0:
            plane_size = 2 * U
            for i in range(U):
                base = i * 2
                p0 = by_plane[base:base+2]
                p1 = by_plane[plane_size + base:plane_size + base+2]
                p2 = by_plane[2*plane_size + base:2*plane_size + base+2]
                p3 = by_plane[3*plane_size + base:3*plane_size + base+2]
                interleaved[i*8:i*8+8] = p0 + p1 + p2 + p3
        else:
            interleaved = by_plane
        return interleaved
    
    elif mode in [0, 2]:
        if len(data) < expected_size * 2:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size * 2}")
            data = data + bytearray(expected_size * 2 - len(data))
        elif len(data) > expected_size * 2:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size * 2}")
            data = data[:expected_size * 2]
        return data
    
    elif mode in [1, 6]:
        if len(data) < expected_size:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size}")
            data = data + bytearray(expected_size - len(data))
        elif len(data) > expected_size:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size}")
            data = data[:expected_size]
        if mode == 6:
            print(f"Mode 6 data for index (truncated): {list(data[:56])}")  # Debug first 56 bytes
        return data
    
    elif mode == -1:
        expected_size = 2 * U * 4
        if len(data) < expected_size // 4:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size // 4}")
            data = data + bytearray((expected_size // 4) - len(data))
        elif len(data) > expected_size // 4:
            print(f"Warning: Data size {len(data)} bytes, expected {expected_size // 4}")
            data = data[:expected_size // 4]
        return data
    
    else:
        raise ValueError(f"Unsupported mode: {mode}")

def simulate_masked_render(interleaved, mode, cols, rows, bitplanes):
    """Simulate masked rendering for modes 0, 1, 2, 3, 5, 6."""
    cell_bytes = 8 if mode in [0, 2, 3, 5] else 4
    cell_px = 16 if mode in [0, 2, 3, 5] else 8
    stride = 40
    
    if mode == 6:
        print(f"Mode 6 bitplane input: {list(interleaved[:56])}")  # Debug input
    for row in range(rows):
        line_offset = row * stride
        for col in range(cols):
            cell_data = interleaved[(row * cols + col) * cell_bytes : (row * cols + col + 1) * cell_bytes]
            mask = 0
            for i in range(4):
                try:
                    val = struct.unpack('>H', cell_data[i*2:i*2+2])[0] if mode in [0, 2, 3, 5] else cell_data[i]
                    mask |= val
                except (struct.error, IndexError):
                    print(f"Warning: Failed to unpack cell data at row {row}, col {col}, mode {mode}")
                    val = 0
            mask = ~mask & ((1 << cell_px) - 1)
            
            for p in range(4):
                plane_offset = line_offset + col * (cell_px // 8)
                try:
                    if mode in [0, 2, 3, 5]:
                        dest_val = struct.unpack('>H', bitplanes[p][plane_offset:plane_offset+2])[0]
                        dest_val &= mask
                        new_val = struct.unpack('>H', cell_data[p*2:p*2+2])[0]
                        dest_val |= new_val
                        bitplanes[p][plane_offset:plane_offset+2] = struct.pack('>H', dest_val)
                    else:
                        dest_val = bitplanes[p][plane_offset]
                        dest_val &= mask & 0xFF
                        new_val = cell_data[p]
                        dest_val |= new_val
                        bitplanes[p][plane_offset] = dest_val
                except (struct.error, IndexError):
                    print(f"Warning: Failed to unpack bitplane data at plane {p}, offset {plane_offset}")
                    continue
    return bitplanes

def simulate_direct_render(data, cols, rows, bitplanes):
    """Simulate direct rendering for mode -1."""
    stride = 40
    U = cols * rows
    for p in range(4):
        plane_data = data[p * (U * 2) : (p + 1) * (U * 2)]
        for row in range(rows):
            offset = row * stride
            try:
                word = struct.unpack('>H', plane_data[row * 2 : row * 2 + 2])[0] if len(plane_data[row * 2 : row * 2 + 2]) == 2 else 0
                bitplanes[p][offset:offset+2] = struct.pack('>H', word)
            except struct.error:
                print(f"Warning: Failed to unpack plane {p}, row {row}, expected 2 bytes, got {len(plane_data[row * 2 : row * 2 + 2])}")
                bitplanes[p][offset:offset+2] = struct.pack('>H', 0)
    return bitplanes

def pixels_from_bitplanes(bitplanes, rows, width_px):
    """Extract 2D pixels from 4 bitplanes."""
    stride = 40
    pixels = []
    for row in range(rows):
        row_pixels = []
        line_offset = row * stride
        for px_byte in range(width_px // 8):
            offset = line_offset + px_byte
            try:
                p0_byte = bitplanes[0][offset]
                p1_byte = bitplanes[1][offset]
                p2_byte = bitplanes[2][offset]
                p3_byte = bitplanes[3][offset]
            except IndexError:
                print(f"Warning: Bitplane index out of range at offset {offset}")
                p0_byte = p1_byte = p2_byte = p3_byte = 0
            for bit in range(7, -1, -1):
                index = ((p0_byte >> bit) & 1) + 2*((p1_byte >> bit) & 1) + 4*((p2_byte >> bit) & 1) + 8*((p3_byte >> bit) & 1)
                row_pixels.append(index)
        pixels.append(row_pixels)
        if row < 14:
            print(f"Pixel row {row}: {row_pixels[:8]}")
    return pixels

def save_bitmap_to_png(pixels, filename, palette, index_name, scale_factor=1):
    """Save 2D pixel array as PNG, scaled by scale_factor."""
    height = len(pixels)
    width = len(pixels[0]) if pixels else 1
    img = Image.new('P', (width, height))
    # Use UI palette for cursor indices if needed (adjust if specific names are cursors)
    selected_palette = WICKED_PALETTE_UI if index_name in ['88', '89', '90', '91', '92', '93'] else WICKED_PALETTE
    img.putpalette([c for rgb in selected_palette for c in rgb])
    for y in range(height):
        for x in range(width):
            img.putpixel((x, y), pixels[y][x])
    # Scale image by scale_factor
    scaled_img = img.resize((width * scale_factor, height * scale_factor), Image.NEAREST)
    scaled_img.save(filename, 'PNG')
    return width * scale_factor, height * scale_factor  # Return scaled dimensions

# Parse command-line arguments
parser = argparse.ArgumentParser(description="Render Amiga graphics to PNG")
parser.add_argument('--scale', type=int, choices=[1, 2], default=1, help="Scale factor for output PNG (1 for normal size, 2 for double size)")
args = parser.parse_args()

# Parse assembler data
try:
    with open('graphicsdata.txt', 'r') as f:
        assembler_data = f.read()
except FileNotFoundError:
    print("Error: 'graphicsdata.txt' not found in current directory")
    exit(1)

indices = parse_assembler_data(assembler_data)

# Process each index
for index_name, info in indices.items():
    mode = info['mode']
    width_minus_1 = info['width_minus_1']
    height_minus_1 = info['height_minus_1']
    data = info['data']
    
    if None in (mode, width_minus_1, height_minus_1):
        print(f"Skipping Index {index_name}: Incomplete metadata (mode={mode}, width_minus_1={width_minus_1}, height_minus_1={height_minus_1})")
        continue
    
    cols = width_minus_1 + 1
    rows = height_minus_1 + 1
    width_px = cols * (16 if mode in [0, 2, 3, 5, -1] else 8)
    
    try:
        # Decode
        decoded = decode_wicked_bitmap(mode, width_minus_1, height_minus_1, data)
        
        # Initialize bitplanes
        bitplanes = [bytearray(40 * rows) for _ in range(4)]
        
        # Render
        if mode in [0, 1, 2, 3, 5, 6]:
            bitplanes = simulate_masked_render(decoded, mode, cols, rows, bitplanes)
        else:
            bitplanes = simulate_direct_render(decoded, cols, rows, bitplanes)
        
        # Extract pixels
        pixels = pixels_from_bitplanes(bitplanes, rows, width_px)
        
        # Save PNG
        filename = f"{index_name}_{args.scale}x.png"
        scaled_width, scaled_height = save_bitmap_to_png(pixels, filename, WICKED_PALETTE, index_name, scale_factor=args.scale)
        print(f"Saved {filename} ({scaled_width}x{scaled_height} pixels, mode {mode}, scaled {args.scale}x)")
        
        # Print all rows for mode 6
        if mode == 6:
            for i, row in enumerate(pixels[:14]):
                print(f"Index {index_name}, Row {i}: {row[:8]}")
            
    except ValueError as e:
        print(f"Error processing Index {index_name}: {e}")
    except Exception as e:
        print(f"Unexpected error in Index {index_name}: {e}")

Create a website or blog at WordPress.com

Up ↑